summaryrefslogtreecommitdiff
path: root/common/accel-lookup.hpp
blob: 18c9ea5150e9f205cfb261ac5d1171b23973d82d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#pragma once

#include "rawaccel-base.hpp"
#include "utility.hpp"

namespace rawaccel {

	struct linear_range {
		double start;
		double stop;
		int num;

		template <typename Func>
		void for_each(Func fn) const
		{
			double interval = (stop - start) / (num - 1);
			for (int i = 0; i < num; i++) {
				fn(i * interval + start);
			}
		}

		int size() const
		{
			return num;
		}
	};


	// represents the range [2^start, 2^stop], with num - 1
	// elements linearly spaced between each exponential step
	struct fp_rep_range {
		int start;
		int stop;
		int num;

		template <typename Func>
		void for_each(Func fn) const
		{
			for (int e = 0; e < stop - start; e++) {
				double exp_scale = scalbn(1, e + start) / num;

				for (int i = 0; i < num; i++) {
					fn((i + num) * exp_scale);
				}
			}

			fn(scalbn(1, stop));
		}

		int size() const
		{
			return (stop - start) * num + 1;
		}
	};

	template <typename Lookup>
	struct lut_base {
		enum { capacity = LUT_CAPACITY };
		using value_t = float;

		template <typename Func>
		void fill(Func fn)
		{
			auto* self = static_cast<Lookup*>(this);

			self->range.for_each([&, fn, i = 0](double x) mutable {
				self->data[i++] = static_cast<value_t>(fn(x));
			});
		}

	};

	struct linear_lut : lut_base<linear_lut> {
		linear_range range;
		bool transfer = false;
		value_t data[capacity] = {};

		double operator()(double x) const
		{
			if (x > range.start) {
				double range_dist = range.stop - range.start;
				double idx_f = (x - range.start) * (range.num - 1) / range_dist;

				unsigned idx = min(static_cast<int>(idx_f), range.size() - 2);

				if (idx < capacity - 1) {
					double y = lerp(data[idx], data[idx + 1], idx_f - idx);
					if (transfer) y /= x;
					return y;
				}
			}

			double y = data[0];
			if (transfer) y /= range.start;
			return y;
		}

		linear_lut(const table_args& args) :
			range({
				args.start,
				args.stop,
				args.num_elements
				}),
			transfer(args.transfer) {}

		linear_lut(const accel_args& args) :
			linear_lut(args.lut_args) {}
	};

	struct binlog_lut : lut_base<binlog_lut> {
		fp_rep_range range;
		double x_start;
		bool transfer = false;
		value_t data[capacity] = {};

		double operator()(double x) const
		{
			int e = min(ilogb(x), range.stop - 1);

			if (e >= range.start) {
				int idx_int_log_part = e - range.start;
				double idx_frac_lin_part = scalbn(x, -e) - 1;
				double idx_f = range.num * (idx_int_log_part + idx_frac_lin_part);

				unsigned idx = min(static_cast<int>(idx_f), range.size() - 2);

				if (idx < capacity - 1) {
					double y = lerp(data[idx], data[idx + 1], idx_f - idx);
					if (transfer) y /= x;
					return y;
				}
			}

			double y = data[0];
			if (transfer) y /= x_start;
			return y;
		}

		binlog_lut(const table_args& args) :
			range({
				static_cast<int>(args.start),
				static_cast<int>(args.stop),
				args.num_elements
				}),
			x_start(scalbn(1, range.start)),
			transfer(args.transfer) {}

		binlog_lut(const accel_args& args) :
			binlog_lut(args.lut_args) {}
	};

}