mesa/src/compiler/nir/nir_builtin_builder.h
Eric R. Smith 4ae192a3d9
Some checks are pending
macOS-CI / macOS-CI (dri) (push) Waiting to run
macOS-CI / macOS-CI (xlib) (push) Waiting to run
glsl, spirv: Improve accuracy of asin() and acos()
The polynomial used for asin_expr() was suboptimal (and its source was
not documented).

A better approximation is found in the _Handbook_of_Mathematical_Functions_
by Abramowitz and Stegun, which is used in Nvidia's Cg toolkit. However,
while this approximation gives a good absolute error bound, its relative
error exceeds the 4096 ulp allowed by the Vulkan spec. Taking a page
from the spirv implementation of asin(), we implement a piecewise
approximation where a Taylor series is used for small values of |x|.
This patch also harmonizes the GLSL and Vulkan implementations by moving
the implementation to common code (nir_builder).

Running tests on asin() with a grid of 64000 samples between 0.0 and +1.0,
the original asin() at 32 bits has:
```
                       glsl                       spirv
  RMSE:            1.756451e-04                 1.609091e-04
  worst abs error: 3.904104e-04 at 0.937001     3.904104e-04 at 0.937001
  worst ulp error: 11800 at 6.2499e-05          3826 at 0.841331
```
whereas the new implementation has for both:
```
  RMSE:            2.528056e-05
  worst abs error: 4.962087e-05 at 0.451149
  worst ulp error: 2379 at 0.215106
```

Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Reviewed-by: Alyssa Rosenzweig <alyssa.rosenzweig@intel.com>
Acked-by: Mel Henning <mhenning@darkrefraction.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40862>
2026-04-21 21:10:22 +00:00

286 lines
8 KiB
C

/*
* Copyright © 2018 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef NIR_BUILTIN_BUILDER_H
#define NIR_BUILTIN_BUILDER_H
#include "util/u_math.h"
#include "nir_builder.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Functions are sorted alphabetically with removed type and "fast" prefix.
* Definitions for functions in the C file come first.
*/
nir_def *nir_cross3(nir_builder *b, nir_def *x, nir_def *y);
nir_def *nir_cross4(nir_builder *b, nir_def *x, nir_def *y);
nir_def *nir_fast_length(nir_builder *b, nir_def *vec);
nir_def *nir_nextafter(nir_builder *b, nir_def *x, nir_def *y);
nir_def *nir_normalize(nir_builder *b, nir_def *vec);
nir_def *nir_smoothstep(nir_builder *b, nir_def *edge0,
nir_def *edge1, nir_def *x);
nir_def *nir_upsample(nir_builder *b, nir_def *hi, nir_def *lo);
nir_def *nir_acos(nir_builder *b, nir_def *x);
nir_def *nir_asin(nir_builder *b, nir_def *x);
nir_def *nir_atan(nir_builder *b, nir_def *y_over_x);
nir_def *nir_atan2(nir_builder *b, nir_def *y, nir_def *x);
nir_def *
nir_build_texture_query(nir_builder *b, nir_tex_instr *tex, nir_texop texop,
unsigned components, nir_alu_type dest_type,
bool include_coord, bool include_lod);
nir_def *
nir_get_texture_lod(nir_builder *b, nir_tex_instr *tex);
nir_def *
nir_get_texture_size(nir_builder *b, nir_tex_instr *tex);
static inline nir_def *
nir_fisnan(nir_builder *b, nir_def *x)
{
unsigned old_fp_math_ctrl = b->fp_math_ctrl;
b->fp_math_ctrl |= nir_fp_exact;
nir_def *res = nir_fneu(b, x, x);
b->fp_math_ctrl = old_fp_math_ctrl;
return res;
}
static inline nir_def *
nir_nan_check2(nir_builder *b, nir_def *x, nir_def *y, nir_def *res)
{
return nir_bcsel(b, nir_fisnan(b, x), x, nir_bcsel(b, nir_fisnan(b, y), y, res));
}
static inline nir_def *
nir_fmax_abs_vec_comp(nir_builder *b, nir_def *vec)
{
nir_def *abs = nir_fabs(b, vec);
nir_def *res = nir_channel(b, abs, 0);
for (unsigned i = 1; i < vec->num_components; ++i)
res = nir_fmax(b, res, nir_channel(b, abs, i));
return res;
}
static inline nir_def *
nir_iabs_diff(nir_builder *b, nir_def *x, nir_def *y)
{
nir_def *cond = nir_ige(b, x, y);
nir_def *res0 = nir_isub(b, x, y);
nir_def *res1 = nir_isub(b, y, x);
return nir_bcsel(b, cond, res0, res1);
}
static inline nir_def *
nir_uabs_diff(nir_builder *b, nir_def *x, nir_def *y)
{
nir_def *cond = nir_uge(b, x, y);
nir_def *res0 = nir_isub(b, x, y);
nir_def *res1 = nir_isub(b, y, x);
return nir_bcsel(b, cond, res0, res1);
}
static inline nir_def *
nir_fexp(nir_builder *b, nir_def *x)
{
return nir_fexp2(b, nir_fmul_imm(b, x, M_LOG2E));
}
static inline nir_def *
nir_flog(nir_builder *b, nir_def *x)
{
return nir_fmul_imm(b, nir_flog2(b, x), 1.0 / M_LOG2E);
}
static inline nir_def *
nir_imad24(nir_builder *b, nir_def *x, nir_def *y, nir_def *z)
{
nir_def *temp = nir_imul24(b, x, y);
return nir_iadd(b, temp, z);
}
static inline nir_def *
nir_imad_hi(nir_builder *b, nir_def *x, nir_def *y, nir_def *z)
{
nir_def *temp = nir_imul_high(b, x, y);
return nir_iadd(b, temp, z);
}
static inline nir_def *
nir_umad_hi(nir_builder *b, nir_def *x, nir_def *y, nir_def *z)
{
nir_def *temp = nir_umul_high(b, x, y);
return nir_iadd(b, temp, z);
}
static inline nir_def *
nir_bitselect(nir_builder *b, nir_def *x, nir_def *y, nir_def *s)
{
return nir_ior(b, nir_iand(b, nir_inot(b, s), x), nir_iand(b, s, y));
}
static inline nir_def *
nir_copysign(nir_builder *b, nir_def *x, nir_def *y)
{
if (b->shader->options->no_integers) {
/* Unlike the integer path, this is not signed zero correct. We assume
* integerless backends don't care.
*/
nir_def *abs = nir_fabs(b, x);
return nir_bcsel(b, nir_flt_imm(b, y, 0.0), nir_fneg(b, abs), abs);
} else {
uint64_t masks = 1ull << (x->bit_size - 1);
uint64_t maskv = ~masks;
nir_def *s = nir_imm_intN_t(b, masks, x->bit_size);
nir_def *v = nir_imm_intN_t(b, maskv, x->bit_size);
return nir_ior(b, nir_iand(b, x, v), nir_iand(b, y, s));
}
}
static inline nir_def *
nir_degrees(nir_builder *b, nir_def *val)
{
return nir_fmul_imm(b, val, 180.0 / M_PI);
}
static inline nir_def *
nir_fdim(nir_builder *b, nir_def *x, nir_def *y)
{
nir_def *cond = nir_flt(b, y, x);
nir_def *res = nir_fsub(b, x, y);
nir_def *zero = nir_imm_floatN_t(b, 0.0, x->bit_size);
// return NaN if either x or y are NaN, else x-y if x>y, else +0.0
return nir_nan_check2(b, x, y, nir_bcsel(b, cond, res, zero));
}
static inline nir_def *
nir_fast_distance(nir_builder *b, nir_def *x, nir_def *y)
{
return nir_fast_length(b, nir_fsub(b, x, y));
}
static inline nir_def *
nir_fast_normalize(nir_builder *b, nir_def *vec)
{
return nir_fdiv(b, vec, nir_fast_length(b, vec));
}
static inline nir_def *
nir_fmad(nir_builder *b, nir_def *x, nir_def *y, nir_def *z)
{
return nir_fadd(b, nir_fmul(b, x, y), z);
}
static inline nir_def *
nir_maxmag(nir_builder *b, nir_def *x, nir_def *y)
{
nir_def *xabs = nir_fabs(b, x);
nir_def *yabs = nir_fabs(b, y);
nir_def *condy = nir_flt(b, xabs, yabs);
nir_def *condx = nir_flt(b, yabs, xabs);
return nir_bcsel(b, condy, y, nir_bcsel(b, condx, x, nir_fmax(b, x, y)));
}
static inline nir_def *
nir_minmag(nir_builder *b, nir_def *x, nir_def *y)
{
nir_def *xabs = nir_fabs(b, x);
nir_def *yabs = nir_fabs(b, y);
nir_def *condx = nir_flt(b, xabs, yabs);
nir_def *condy = nir_flt(b, yabs, xabs);
return nir_bcsel(b, condy, y, nir_bcsel(b, condx, x, nir_fmin(b, x, y)));
}
static inline nir_def *
nir_nan(nir_builder *b, nir_def *x)
{
nir_def *nan = nir_imm_floatN_t(b, NAN, x->bit_size);
if (x->num_components == 1)
return nan;
nir_def *nans[NIR_MAX_VEC_COMPONENTS];
for (unsigned i = 0; i < x->num_components; ++i)
nans[i] = nan;
return nir_vec(b, nans, x->num_components);
}
static inline nir_def *
nir_radians(nir_builder *b, nir_def *val)
{
return nir_fmul_imm(b, val, M_PI / 180.0);
}
static inline nir_def *
nir_select(nir_builder *b, nir_def *x, nir_def *y, nir_def *s)
{
if (s->num_components != 1) {
uint64_t mask = 1ull << (s->bit_size - 1);
s = nir_iand_imm(b, s, mask);
}
return nir_bcsel(b, nir_ieq_imm(b, s, 0), x, y);
}
static inline nir_def *
nir_ftan(nir_builder *b, nir_def *x)
{
return nir_fdiv(b, nir_fsin(b, x), nir_fcos(b, x));
}
static inline nir_def *
nir_clz_u(nir_builder *b, nir_def *a)
{
nir_def *val;
val = nir_isub_imm(b, a->bit_size - 1,
nir_ufind_msb(b, nir_u2uN(b, a,
MAX2(a->bit_size, 32))));
return nir_u2uN(b, val, a->bit_size);
}
static inline nir_def *
nir_ctz_u(nir_builder *b, nir_def *a)
{
nir_def *cond = nir_ieq_imm(b, a, 0);
return nir_bcsel(b, cond,
nir_imm_intN_t(b, a->bit_size, a->bit_size),
nir_u2uN(b, nir_find_lsb(b, a), a->bit_size));
}
#ifdef __cplusplus
}
#endif
#endif /* NIR_BUILTIN_BUILDER_H */