1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2021 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
9*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
10*c8dee2aaSAndroid Build Coastguard Worker #include <cstdio>
11*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
12*c8dee2aaSAndroid Build Coastguard Worker
13*c8dee2aaSAndroid Build Coastguard Worker #include "experimental/lowp-basic/QMath.h"
14*c8dee2aaSAndroid Build Coastguard Worker
15*c8dee2aaSAndroid Build Coastguard Worker struct Stats {
16*c8dee2aaSAndroid Build Coastguard Worker int64_t diff_8_bits = 0;
17*c8dee2aaSAndroid Build Coastguard Worker int64_t max_diff = 0;
18*c8dee2aaSAndroid Build Coastguard Worker int64_t min_diff = 0;
19*c8dee2aaSAndroid Build Coastguard Worker int64_t total = 0;
20*c8dee2aaSAndroid Build Coastguard Worker
logStats21*c8dee2aaSAndroid Build Coastguard Worker void log(int16_t golden, int16_t candidate) {
22*c8dee2aaSAndroid Build Coastguard Worker int64_t diff = candidate - golden;
23*c8dee2aaSAndroid Build Coastguard Worker max_diff = std::max(max_diff, diff);
24*c8dee2aaSAndroid Build Coastguard Worker min_diff = std::min(min_diff, diff);
25*c8dee2aaSAndroid Build Coastguard Worker diff_8_bits += candidate != golden;
26*c8dee2aaSAndroid Build Coastguard Worker total++;
27*c8dee2aaSAndroid Build Coastguard Worker }
28*c8dee2aaSAndroid Build Coastguard Worker
printStats29*c8dee2aaSAndroid Build Coastguard Worker void print() const {
30*c8dee2aaSAndroid Build Coastguard Worker printf("8-bit diff: %lld - %g%%\n", diff_8_bits, 100.0 * diff_8_bits / total);
31*c8dee2aaSAndroid Build Coastguard Worker printf("differences min: %lld max: %lld\n", min_diff, max_diff);
32*c8dee2aaSAndroid Build Coastguard Worker printf("total: %lld\n", total);
33*c8dee2aaSAndroid Build Coastguard Worker }
34*c8dee2aaSAndroid Build Coastguard Worker };
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker // This has all kinds of rounding issues.
37*c8dee2aaSAndroid Build Coastguard Worker // TODO(herb): figure out rounding problems with this code.
golden_bilerp(float tx,float ty,int16_t p00,int16_t p10,int16_t p01,int16_t p11)38*c8dee2aaSAndroid Build Coastguard Worker static float golden_bilerp(float tx, float ty, int16_t p00, int16_t p10, int16_t p01, int16_t p11) {
39*c8dee2aaSAndroid Build Coastguard Worker return (1.0f-tx) * (1.0f-ty) * p00
40*c8dee2aaSAndroid Build Coastguard Worker + (1.0f-tx) * ty * p01
41*c8dee2aaSAndroid Build Coastguard Worker + (1.0f-ty) * tx * p10
42*c8dee2aaSAndroid Build Coastguard Worker + tx * ty * p11;
43*c8dee2aaSAndroid Build Coastguard Worker }
44*c8dee2aaSAndroid Build Coastguard Worker
golden_bilerp2(float tx,float ty,int16_t p00,int16_t p10,int16_t p01,int16_t p11)45*c8dee2aaSAndroid Build Coastguard Worker static double golden_bilerp2(
46*c8dee2aaSAndroid Build Coastguard Worker float tx, float ty, int16_t p00, int16_t p10, int16_t p01, int16_t p11) {
47*c8dee2aaSAndroid Build Coastguard Worker // Double is needed to avoid rounding of lower bits.
48*c8dee2aaSAndroid Build Coastguard Worker double dtx(tx), dty(ty);
49*c8dee2aaSAndroid Build Coastguard Worker
50*c8dee2aaSAndroid Build Coastguard Worker double top = (1.0 - dtx) * p00 + dtx * p10;
51*c8dee2aaSAndroid Build Coastguard Worker double bottom = (1.0 - dtx) * p01 + dtx * p11;
52*c8dee2aaSAndroid Build Coastguard Worker
53*c8dee2aaSAndroid Build Coastguard Worker return (1.0 - dty) * top + dty * bottom;
54*c8dee2aaSAndroid Build Coastguard Worker }
55*c8dee2aaSAndroid Build Coastguard Worker
full_res_bilerp(float tx,float ty,int16_t p00,int16_t p10,int16_t p01,int16_t p11)56*c8dee2aaSAndroid Build Coastguard Worker static int16_t full_res_bilerp(
57*c8dee2aaSAndroid Build Coastguard Worker float tx, float ty, int16_t p00, int16_t p10, int16_t p01, int16_t p11) {
58*c8dee2aaSAndroid Build Coastguard Worker int32_t ftx(floor(tx * 65536.0f + 0.5f));
59*c8dee2aaSAndroid Build Coastguard Worker int64_t top = ftx * (p10 - p00) + 65536 * p00;
60*c8dee2aaSAndroid Build Coastguard Worker int64_t bottom = ftx * (p11 - p01) + 65536 * p01;
61*c8dee2aaSAndroid Build Coastguard Worker
62*c8dee2aaSAndroid Build Coastguard Worker int64_t fty(floor(ty * 65536.0f + 0.5f));
63*c8dee2aaSAndroid Build Coastguard Worker int64_t temp = fty * (bottom - top) + top * 65536LL;
64*c8dee2aaSAndroid Build Coastguard Worker int64_t rounded = temp + (1LL << 31);
65*c8dee2aaSAndroid Build Coastguard Worker return rounded >> 32;
66*c8dee2aaSAndroid Build Coastguard Worker }
67*c8dee2aaSAndroid Build Coastguard Worker
68*c8dee2aaSAndroid Build Coastguard Worker
bilerp_1(float tx,float ty,int16_t p00,int16_t p10,int16_t p01,int16_t p11)69*c8dee2aaSAndroid Build Coastguard Worker static int16_t bilerp_1(float tx, float ty, int16_t p00, int16_t p10, int16_t p01, int16_t p11) {
70*c8dee2aaSAndroid Build Coastguard Worker const int logPixelScale = 7;
71*c8dee2aaSAndroid Build Coastguard Worker const int16_t half = 1 << logPixelScale;
72*c8dee2aaSAndroid Build Coastguard Worker I16 qtx = floor(tx * 65536.0f - 32768.0f + 0.5f);
73*c8dee2aaSAndroid Build Coastguard Worker I16 qw = (p10 - p00) << logPixelScale;
74*c8dee2aaSAndroid Build Coastguard Worker U16 qm = (p10 + p00) << logPixelScale;
75*c8dee2aaSAndroid Build Coastguard Worker I16 top = (I16)((U16)(constrained_add(simulate_ssse3_mm_mulhrs_epi16(qtx, qw), qm) + 1) >> 1);
76*c8dee2aaSAndroid Build Coastguard Worker
77*c8dee2aaSAndroid Build Coastguard Worker qw = (p11 - p01) << logPixelScale;
78*c8dee2aaSAndroid Build Coastguard Worker qm = (p11 + p01) << logPixelScale;
79*c8dee2aaSAndroid Build Coastguard Worker I16 bottom =
80*c8dee2aaSAndroid Build Coastguard Worker (I16)((U16)(constrained_add(simulate_ssse3_mm_mulhrs_epi16(qtx, qw), qm) + 1) >> 1);
81*c8dee2aaSAndroid Build Coastguard Worker
82*c8dee2aaSAndroid Build Coastguard Worker I16 qty = floor(ty * 65536.0f - 32768.0f + 0.5f);
83*c8dee2aaSAndroid Build Coastguard Worker
84*c8dee2aaSAndroid Build Coastguard Worker qw = bottom - top;
85*c8dee2aaSAndroid Build Coastguard Worker qm = (U16)bottom + (U16)top;
86*c8dee2aaSAndroid Build Coastguard Worker U16 scaledAnswer = constrained_add(simulate_ssse3_mm_mulhrs_epi16(qty, qw), qm);
87*c8dee2aaSAndroid Build Coastguard Worker
88*c8dee2aaSAndroid Build Coastguard Worker return (scaledAnswer[0] + half) >> (logPixelScale + 1);
89*c8dee2aaSAndroid Build Coastguard Worker }
90*c8dee2aaSAndroid Build Coastguard Worker
91*c8dee2aaSAndroid Build Coastguard Worker template <typename Bilerp>
check_bilerp(Bilerp bilerp)92*c8dee2aaSAndroid Build Coastguard Worker static Stats check_bilerp(Bilerp bilerp) {
93*c8dee2aaSAndroid Build Coastguard Worker Stats stats;
94*c8dee2aaSAndroid Build Coastguard Worker const int step = 1;
95*c8dee2aaSAndroid Build Coastguard Worker auto interesting = {0, 1, 2, 3, 4, 5, 6, 7, 8, 60, 61, 62, 63, 64, 65, 66, 67, 68, 124, 125,
96*c8dee2aaSAndroid Build Coastguard Worker 126, 127, 128, 129, 130, 131, 132, 188, 189, 190, 191, 192, 193, 194,
97*c8dee2aaSAndroid Build Coastguard Worker 195, 196, 248, 249, 250, 251, 252, 253, 254, 255};
98*c8dee2aaSAndroid Build Coastguard Worker for (float tx : {0.0f, 0.25f, 0.5f, 0.75f, 1.0f - 1.0f/65536.0f})
99*c8dee2aaSAndroid Build Coastguard Worker for (float ty : {0.0f, 0.25f, 0.5f, 0.75f, 1.0f - 1.0f/65536.0f})
100*c8dee2aaSAndroid Build Coastguard Worker for (int p00 : interesting)
101*c8dee2aaSAndroid Build Coastguard Worker for (int p01 : interesting)
102*c8dee2aaSAndroid Build Coastguard Worker for (int p10 : interesting)
103*c8dee2aaSAndroid Build Coastguard Worker for (int p11 : interesting) {
104*c8dee2aaSAndroid Build Coastguard Worker // Having this be double causes the proper rounding.
105*c8dee2aaSAndroid Build Coastguard Worker double l = golden_bilerp2(tx, ty, p00, p10, p01, p11);
106*c8dee2aaSAndroid Build Coastguard Worker int16_t golden = floor(l + 0.5);
107*c8dee2aaSAndroid Build Coastguard Worker //l = golden_bilerp(tx, ty, p00, p10, p01, p11);
108*c8dee2aaSAndroid Build Coastguard Worker //int16_t golden2 = floor(l + 0.5f);
109*c8dee2aaSAndroid Build Coastguard Worker int16_t candidate = bilerp(tx, ty, p00, p10, p01, p11);
110*c8dee2aaSAndroid Build Coastguard Worker stats.log(golden, candidate);
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker return stats;
113*c8dee2aaSAndroid Build Coastguard Worker }
114*c8dee2aaSAndroid Build Coastguard Worker
main()115*c8dee2aaSAndroid Build Coastguard Worker int main() {
116*c8dee2aaSAndroid Build Coastguard Worker Stats stats;
117*c8dee2aaSAndroid Build Coastguard Worker
118*c8dee2aaSAndroid Build Coastguard Worker printf("\nUsing trunc_bilerp...\n");
119*c8dee2aaSAndroid Build Coastguard Worker stats = check_bilerp(bilerp_1);
120*c8dee2aaSAndroid Build Coastguard Worker stats.print();
121*c8dee2aaSAndroid Build Coastguard Worker
122*c8dee2aaSAndroid Build Coastguard Worker printf("\nUsing full_res_bilerp...\n");
123*c8dee2aaSAndroid Build Coastguard Worker stats = check_bilerp(full_res_bilerp);
124*c8dee2aaSAndroid Build Coastguard Worker stats.print();
125*c8dee2aaSAndroid Build Coastguard Worker
126*c8dee2aaSAndroid Build Coastguard Worker printf("Done.\n");
127*c8dee2aaSAndroid Build Coastguard Worker return 0;
128*c8dee2aaSAndroid Build Coastguard Worker }
129