1 /*
2 * Copyright © 2021 Emma Anholt
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "compiler/nir/nir.h"
25 #include "compiler/nir/nir_builder.h"
26 #include "i915_fpc.h"
27
28 static bool
i915_sincos_filter(const nir_instr * instr,const void * data)29 i915_sincos_filter(const nir_instr *instr, const void *data)
30 {
31 if (instr->type != nir_instr_type_alu)
32 return false;
33
34 switch (nir_instr_as_alu(instr)->op) {
35 case nir_op_fcos:
36 case nir_op_fsin:
37 return true;
38 default:
39 return false;
40 }
41 }
42
43 /* Compute sin using a quadratic and quartic. It gives continuity
44 * that repeating the Taylor series lacks every 2*pi, and has
45 * reduced error.
46 *
47 * The idea was described at:
48 * https://web.archive.org/web/20100613230051/http://www.devmaster.net/forums/showthread.php?t=5784
49 */
50 static nir_ssa_def *
i915_sincos_lower(nir_builder * b,nir_instr * instr,void * data)51 i915_sincos_lower(nir_builder *b, nir_instr *instr, void *data)
52 {
53 nir_alu_instr *alu = nir_instr_as_alu(instr);
54 nir_ssa_def *x = nir_ssa_for_alu_src(b, alu, 0);
55
56 /* Reduce range from repeating about [-pi,pi] to [-1,1] */
57 x = nir_fmul_imm(b, x, M_1_PI / 2.0);
58 if (alu->op == nir_op_fsin)
59 x = nir_fadd_imm(b, x, 0.5);
60 else
61 x = nir_fadd_imm(b, x, 0.75);
62 x = nir_ffract(b, x);
63 x = nir_fadd_imm(b, nir_fmul_imm(b, x, 2.0), -1.0);
64
65 nir_ssa_def *x_absx = nir_fmul(b, x, nir_fabs(b, x));
66
67 /* y is the first approximation of the result. */
68 nir_ssa_def *y =
69 nir_fadd(b, nir_fmul_imm(b, x, 4.0), nir_fmul_imm(b, x_absx, -4.0));
70
71 /* improve the accuracy. */
72 nir_ssa_def *y_absy = nir_fmul(b, y, nir_fabs(b, y));
73 return nir_fadd(b, nir_fmul_imm(b, nir_fsub(b, y_absy, y), 0.225), y);
74 }
75
76 bool
i915_nir_lower_sincos(nir_shader * s)77 i915_nir_lower_sincos(nir_shader *s)
78 {
79 return nir_shader_lower_instructions(s, i915_sincos_filter,
80 i915_sincos_lower, NULL);
81 }
82