shader: emit float min/max as host instructions on AMD GPUs (#1761)

* shader: emit float min/max as host instructions on AMD GPUs

V_MIN_F32, V_MAX_F32, V_MIN3_F32 and V_MAX3_F32 were emulated with a
classify-and-select sequence of about a dozen instructions each, to give
the guest's NaN and signed-zero results on any host. AMD GPUs execute
GLSL.std.450 NMin/NMax with those same rules, so emit them directly when
the device is AMD and supports VK_KHR_shader_float_controls2. Each
instruction is decorated FPFastMathMode None, since
SignedZeroInfNanPreserve does not cover extended instructions. MED3 keeps
its exact emulation. KYTY_EMULATED_FLOAT_MIN_MAX=1 keeps the emulation.

In SAROS the two heaviest compute shaders get 37% and 59% faster on an
RX 9070 XT (RADV), about 4 ms of GPU time per frame.

Adds VectorMinMaxF32RuntimeEdges (runtime operands, so nothing is folded)
and a --float-min-max-only mode that runs it and
VectorMinMaxF32NanAndSignedZeroEdges with the emulation and, where
supported, with host instructions.

Developed with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* shader: gate host float min/max on RADV and check the emitted instructions

* shader: drop float controls 2 from host float min/max

* shader: drop the float min/max env override and atomic flag

* shader: pass host capabilities to the SPIR-V emitter as a profile built once per device

* shader: enable host float min/max on AMD GPUs

---------

Co-authored-by: arda <ardaeska@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
Arda Sahin
2026-10-21 19:36:08 +00:00
committed by GitHub
parent c07ad621f2
commit 286481889f
11 changed files with 141 additions and 23 deletions
@@ -5,6 +5,7 @@
#include "common/stringUtils.h"
#include "graphics/shader/recompiler/BufferFormat.h"
#include "graphics/shader/recompiler/backend/spirv/SpirvBuilder.h"
#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h"
#include "graphics/shader/recompiler/ir/ShaderIR.h"
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
@@ -73,9 +74,9 @@ struct ImageDefinition {
};
struct EmitterState {
EmitterState(IR::Program& program_, ShaderStageInputInfo input_info_, bool maintenance8_enabled_)
EmitterState(IR::Program& program_, ShaderStageInputInfo input_info_, const Profile& profile_)
: builder(program_.stage == ShaderType::Mesh ? 0x00010400u : 0x00010300u),
program(program_), input_info(input_info_), maintenance8_enabled(maintenance8_enabled_) {
program(program_), input_info(input_info_), profile(profile_) {
if (ShaderWorkgroupInput(program.stage, input_info) != nullptr) {
lds_storage_class = spv::StorageClassWorkgroup;
}
@@ -88,7 +89,7 @@ struct EmitterState {
Builder builder;
IR::Program& program;
ShaderStageInputInfo input_info;
bool maintenance8_enabled;
const Profile& profile;
uint32_t debug_source = 0;
uint32_t void_type = 0;
uint32_t bool_type = 0;