engine/rr: cheaper bounds checks and conversion guards in the translated passes
- ptx2rr.py fetch0 (the translation's bounds-checked texel fetch): one unsigned comparison per axis instead of two signed ones; a negative coordinate is above every size. enc0's gather fetches 102 times: 231 instructions fewer, scalar spills 200 -> 176. - The output passes (post, down, downp, pooled) also go through spv_exact_convert.py now and take ptx2rr.py --exact-convert's single f32 guard on binary16 conversions, as the hybrid stages do. Their ISA already uses v_fma_mix*_f16 for plain conversions, so the check is the whole instruction mix: only v_and_or_b32 / v_or / v_perm / shifts go (post -16 VALU, down -9, downp -73); every v_fma_f32 / v_mul_f32 / v_add_f32 / conversion / v_exp / v_log count is unchanged. Every image byte-identical on the replay inputs (also robustness2 and the split gather build). RX 9070 XT, 720p -> 1440p: enc0 745 -> 738 us, output 673 -> 665 us. Signed-off-by: itchyfeetleech <[email protected]>
This commit is contained in:
1 parent
e46c589c68
commit
69a4f6ae2f
3 files changed
+16
-6
No files matched your search
@@ -168,8 +168,9 @@ def main():
|
|||||||
cmd += [f'--buffer={off}' for off in BUFFERS.get(stage, [])]
|
cmd += [f'--buffer={off}' for off in BUFFERS.get(stage, [])]
|
||||||
cmd += [f'--snorm16={off}' for off in SNORM16.get(stage, [])]
|
cmd += [f'--snorm16={off}' for off in SNORM16.get(stage, [])]
|
||||||
cmd += [f'--surface={off}={fmt}' for off, fmt in SURFACES.get(stage, {}).items()]
|
cmd += [f'--surface={off}={fmt}' for off, fmt in SURFACES.get(stage, {}).items()]
|
||||||
# the hybrid stages' modules go through spv_exact_convert.py, so their conversions need only the lighter guard
|
# the hybrid stages' and the output passes' modules go through spv_exact_convert.py, so their conversions need
|
||||||
exact = bool(a.native) and stage in HYBRID
|
# only the lighter guard (ptx2rr.py --exact-convert)
|
||||||
|
exact = (bool(a.native) and stage in HYBRID) or not network
|
||||||
cmd += ['--exact-convert'] if exact else []
|
cmd += ['--exact-convert'] if exact else []
|
||||||
subprocess.run(cmd, check=True, stderr=subprocess.DEVNULL)
|
subprocess.run(cmd, check=True, stderr=subprocess.DEVNULL)
|
||||||
info = json.loads(generated.with_suffix('.json').read_text())
|
info = json.loads(generated.with_suffix('.json').read_text())
|
||||||
@@ -210,6 +211,9 @@ def main():
|
|||||||
else:
|
else:
|
||||||
subprocess.run(capped + ['glslangValidator', '-V', '--target-env', 'vulkan1.3', str(generated), '-o',
|
subprocess.run(capped + ['glslangValidator', '-V', '--target-env', 'vulkan1.3', str(generated), '-o',
|
||||||
str(out / f'{stage}.spv')], check=True, stdout=subprocess.DEVNULL)
|
str(out / f'{stage}.spv')], check=True, stdout=subprocess.DEVNULL)
|
||||||
|
if exact:
|
||||||
|
subprocess.run([sys.executable, str(HERE / 'rr' / 'spv_exact_convert.py'), str(out / f'{stage}.spv')],
|
||||||
|
check=True, stdout=subprocess.DEVNULL)
|
||||||
if hybrid:
|
if hybrid:
|
||||||
pass
|
pass
|
||||||
elif network:
|
elif network:
|
||||||
|
|||||||
+8
-3
@@ -552,9 +552,14 @@ registers to the waves its shared memory leaves room for (enc1 needed 97 vector
|
|||||||
`float(float16_t(x))`, so a `cvt.rn.f16.f32` keeps only the opaque zero on its f32 bits (against the fused
|
`float(float16_t(x))`, so a `cvt.rn.f16.f32` keeps only the opaque zero on its f32 bits (against the fused
|
||||||
multiply-convert, which `precise` alone does not stop: without it ACO emits `v_fma_mixlo_f16` there). In the ISA the
|
multiply-convert, which `precise` alone does not stop: without it ACO emits `v_fma_mixlo_f16` there). In the ISA the
|
||||||
same number of `v_cvt_f16_f32` / `v_cvt_f32_f16` as before and no `v_fma_mix*_f16`; 224 / 153 fewer instructions:
|
same number of `v_cvt_f16_f32` / `v_cvt_f32_f16` as before and no `v_fma_mix*_f16`; 224 / 153 fewer instructions:
|
||||||
enc0 747 -> 729 us, dec0_1 598 -> 592. The output passes keep both ORs (they are not passed through
|
enc0 747 -> 729 us, dec0_1 598 -> 592. The output passes (post, down, downp, pooled) now go through
|
||||||
`spv_exact_convert.py`, and their ISA already uses `v_fma_mix*_f16` for plain conversions, so it cannot be checked
|
`spv_exact_convert.py` too and take the same guard. Their ISA already uses `v_fma_mix*_f16` for plain conversions,
|
||||||
the same way).
|
so the check there is the whole instruction mix: only `v_and_or_b32` / `v_or` / `v_perm` / shifts go (post -16
|
||||||
|
VALU, down -9, downp -73), every `v_fma_f32` / `v_mul_f32` / `v_add_f32` / conversion / `v_exp` / `v_log` count is
|
||||||
|
unchanged (post moves 5 of its 57 mixes from the low to the high half of their registers). Output 673 -> 665 us.
|
||||||
|
- **One unsigned comparison per axis in the translation's bounds-checked fetch** (`ptx2rr.py` `fetch0`: a negative
|
||||||
|
coordinate is above every size): 231 instructions fewer in enc0, whose gather fetches 102 times, its scalar spills
|
||||||
|
200 -> 176; enc0 745 -> 738 us.
|
||||||
- **Hardware saturation for the FP8 conversions of f32 values** (`rr_exact.glsl` `FINITE`). RADV lowers a saturating
|
- **Hardware saturation for the FP8 conversions of f32 values** (`rr_exact.glsl` `FINITE`). RADV lowers a saturating
|
||||||
conversion (`saturatedConvertEXT`) as a NaN-preserving clamp per element (`v_minimummaximum_f32`) and then the
|
conversion (`saturatedConvertEXT`) as a NaN-preserving clamp per element (`v_minimummaximum_f32`) and then the
|
||||||
conversion, because the hardware's FP16_OVFL saturation turns infinities into NaN instead of +-448 (ACO's comment;
|
conversion, because the hardware's FP16_OVFL saturation turns infinities into NaN instead of +-448 (ACO's comment;
|
||||||
|
|||||||
+2
-1
@@ -70,7 +70,8 @@ uint HU2_(u16vec2 u) { return uint(u.x) | (uint(u.y) << 16); }
|
|||||||
uint H2U(f16vec2 h) { return HU2_(float16BitsToUint16(h)); }
|
uint H2U(f16vec2 h) { return HU2_(float16BitsToUint16(h)); }
|
||||||
int s16(uint u) { return (int(u) << 16) >> 16; }
|
int s16(uint u) { return (int(u) << 16) >> 16; }
|
||||||
float rcp_a(float x) { return 1.0 / x; }
|
float rcp_a(float x) { return 1.0 / x; }
|
||||||
vec4 fetch0(sampler2D t, ivec2 c) { const ivec2 n = textureSize(t, 0); return (c.x < 0 || c.y < 0 || c.x >= n.x || c.y >= n.y) ? vec4(0.0) : texelFetch(t, c, 0); }
|
// (one unsigned comparison per axis: a negative coordinate is above every size)
|
||||||
|
vec4 fetch0(sampler2D t, ivec2 c) { const ivec2 n = textureSize(t, 0); return any(greaterThanEqual(uvec2(c), uvec2(n))) ? vec4(0.0) : texelFetch(t, c, 0); }
|
||||||
// x + copysign(0.5, x) rounded toward zero and then truncated: round half away from zero, exactly
|
// x + copysign(0.5, x) rounded toward zero and then truncated: round half away from zero, exactly
|
||||||
float round_half_away(float x) { float t = trunc(x); return abs(x - t) >= 0.5 ? t + sign(x) : t; }
|
float round_half_away(float x) { float t = trunc(x); return abs(x - t) >= 0.5 ? t + sign(x) : t; }
|
||||||
uint PARAM32(uint o) { return param.w[o >> 2]; }
|
uint PARAM32(uint o) { return param.w[o >> 2]; }
|
||||||
|
|||||||
Reference in new issue
Block a user