1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "shared.rsh" 18 19// Has the same kernels as reduce_backward.rs, plus some others. 20// 21// This test case places the pragmas before the functions (forward 22// reference), and the other test case places the pragmas after the 23// functions (backward reference). 24 25float negInf, posInf; 26 27static bool IsNaN(float v) { 28 // a NaN (and only a NaN) compares unequal to everything 29 return v != v; 30} 31 32///////////////////////////////////////////////////////////////////////// 33 34#pragma rs reduce(addint) \ 35 accumulator(aiAccum) 36 37static void aiAccum(int *accum, int val) { *accum += val; } 38 39///////////////////////////////////////////////////////////////////////// 40 41// These kernels find an input value of minimum absolute value. 42// 43// If the input domain consists of all non-NaN values (including 44// infinities), we cannot pick an initializer from the input domain, 45// because there are two different members of the domain with maximum 46// absolute value -- positive and negative infinity. Instead, we need 47// to pick some other distinguished initializer, and explicitly check 48// for and handle an accumulator with this distinguished value. 49// 50// The two kernels represent the distinguished value differently. 51 52//....................................................................... 53 54// The kernel findMinAbsNaN uses an initializer from outside the input 55// domain that is nonetheless representable as a float -- NaN. 56 57#pragma rs reduce(findMinAbsNaN) \ 58 initializer(fMinAbsNaNInit) accumulator(fMinAbsNaNAccumulator) combiner(fMinAbsNaNCombiner) 59 60static void fMinAbsNaNInit(float *accum) { 61 *accum = nan(0); 62} 63 64static void fMinAbsNaNAccumulator(float *accum, float val) { 65 if (IsNaN(*accum) || (fabs(val) < fabs(*accum))) 66 *accum = val; 67} 68 69static void fMinAbsNaNCombiner(float *accum, const float *other) { 70 if (!IsNaN(*other)) 71 fMinAbsNaNAccumulator(accum, *other); 72} 73 74//....................................................................... 75 76// The kernel findMinAbsBool represents its accumulator as a struct 77// with two fields -- a bool field to indicate whether or not the 78// accumulator has the distinguished initial value, and a float field 79// for a non-initial value. 80 81typedef struct FindMinAbsBoolAccumType { 82 // set to true by initializer function; 83 // set to false by accumulator function 84 bool onlyInitialized; 85 // only valid when onlyInitialized is false 86 float val; 87} FindMinAbsBoolAccumType; 88 89#pragma rs reduce(findMinAbsBool) \ 90 initializer(fMinAbsBoolInit) accumulator(fMinAbsBoolAccumulator) combiner(fMinAbsBoolCombiner) \ 91 outconverter(fMinAbsBoolOut) 92 93static void fMinAbsBoolInit(FindMinAbsBoolAccumType *accum) { 94 accum->onlyInitialized = true; 95} 96 97static void fMinAbsBoolAccumulator(FindMinAbsBoolAccumType *accum, float val) { 98 if (accum->onlyInitialized || (fabs(val) < fabs(accum->val))) 99 accum->val = val; 100 accum->onlyInitialized = false; 101} 102 103static void fMinAbsBoolCombiner(FindMinAbsBoolAccumType *accum, const FindMinAbsBoolAccumType *other) { 104 if (!other->onlyInitialized) 105 fMinAbsBoolAccumulator(accum, other->val); 106} 107 108static void fMinAbsBoolOut(float *out, const FindMinAbsBoolAccumType *accum) { 109 *out = accum->val; 110} 111 112///////////////////////////////////////////////////////////////////////// 113 114#pragma rs reduce(findMinAndMax) \ 115 initializer(fMMInit) accumulator(fMMAccumulator) \ 116 combiner(fMMCombiner) outconverter(fMMOutConverter) 117 118typedef struct { 119 float val; 120 int idx; 121} IndexedVal; 122 123typedef struct { 124 IndexedVal min, max; 125} MinAndMax; 126 127static void fMMInit(MinAndMax *accum) { 128 accum->min.val = posInf; 129 accum->min.idx = -1; 130 accum->max.val = negInf; 131 accum->max.idx = -1; 132} 133 134static void fMMAccumulator(MinAndMax *accum, float in, int x) { 135 IndexedVal me; 136 me.val = in; 137 me.idx = x; 138 139 if (me.val <= accum->min.val) 140 accum->min = me; 141 if (me.val >= accum->max.val) 142 accum->max = me; 143} 144 145static void fMMCombiner(MinAndMax *accum, 146 const MinAndMax *val) { 147 if ((accum->min.idx < 0) || (val->min.val < accum->min.val)) 148 accum->min = val->min; 149 if ((accum->max.idx < 0) || (val->max.val > accum->max.val)) 150 accum->max = val->max; 151} 152 153static void fMMOutConverter(int2 *result, 154 const MinAndMax *val) { 155 result->x = val->min.idx; 156 result->y = val->max.idx; 157} 158 159///////////////////////////////////////////////////////////////////////// 160 161#pragma rs reduce(fz) \ 162 initializer(fzInit) \ 163 accumulator(fzAccum) combiner(fzCombine) 164 165static void fzInit(int *accumIdx) { *accumIdx = -1; } 166 167static void fzAccum(int *accumIdx, 168 int inVal, int x /* special arg */) { 169 if (inVal==0) *accumIdx = x; 170} 171 172static void fzCombine(int *accumIdx, const int *accumIdx2) { 173 if (*accumIdx2 >= 0) *accumIdx = *accumIdx2; 174} 175 176///////////////////////////////////////////////////////////////////////// 177 178#pragma rs reduce(fz2) \ 179 initializer(fz2Init) \ 180 accumulator(fz2Accum) combiner(fz2Combine) 181 182static void fz2Init(int2 *accum) { accum->x = accum->y = -1; } 183 184static void fz2Accum(int2 *accum, 185 int inVal, 186 int x /* special arg */, 187 int y /* special arg */) { 188 if (inVal==0) { 189 accum->x = x; 190 accum->y = y; 191 } 192} 193 194static void fz2Combine(int2 *accum, const int2 *accum2) { 195 if (accum2->x >= 0) *accum = *accum2; 196} 197 198///////////////////////////////////////////////////////////////////////// 199 200#pragma rs reduce(fz3) \ 201 initializer(fz3Init) \ 202 accumulator(fz3Accum) combiner(fz3Combine) 203 204static void fz3Init(int3 *accum) { accum->x = accum->y = accum->z = -1; } 205 206static void fz3Accum(int3 *accum, 207 int inVal, 208 int x /* special arg */, 209 int y /* special arg */, 210 int z /* special arg */) { 211 if (inVal==0) { 212 accum->x = x; 213 accum->y = y; 214 accum->z = z; 215 } 216} 217 218static void fz3Combine(int3 *accum, const int3 *accum2) { 219 if (accum2->x >= 0) *accum = *accum2; 220} 221 222///////////////////////////////////////////////////////////////////////// 223 224#pragma rs reduce(histogram) \ 225 accumulator(hsgAccum) combiner(hsgCombine) 226 227#define BUCKETS 256 228typedef uint32_t Histogram[BUCKETS]; 229 230static void hsgAccum(Histogram *h, uchar in) { ++(*h)[in]; } 231 232static void hsgCombine(Histogram *accum, const Histogram *addend) { 233 for (int i = 0; i < BUCKETS; ++i) 234 (*accum)[i] += (*addend)[i]; 235} 236 237#pragma rs reduce(mode) \ 238 accumulator(hsgAccum) combiner(hsgCombine) \ 239 outconverter(modeOutConvert) 240 241static void modeOutConvert(int2 *result, const Histogram *h) { 242 uint32_t mode = 0; 243 for (int i = 1; i < BUCKETS; ++i) 244 if ((*h)[i] > (*h)[mode]) mode = i; 245 result->x = mode; 246 result->y = (*h)[mode]; 247} 248 249///////////////////////////////////////////////////////////////////////// 250 251#pragma rs reduce(sumgcd) accumulator(sgAccum) combiner(sgCombine) 252 253static int gcd(int a, int b) { 254 while (b != 0) { 255 const int aNew = b; 256 const int bNew = a % b; 257 258 a = aNew; 259 b = bNew; 260 } 261 return a; 262} 263 264static void sgAccum(long *accum, int a, int b) { 265 *accum += gcd(a, b); 266} 267 268static void sgCombine(long *accum, const long *other) { *accum += *other; } 269 270///////////////////////////////////////////////////////////////////////// 271 272// These two kernels have anonymous result types that are equivalent. 273// slang doesn't common them (i.e., each gets its own RSExportType); 274// so Java reflection must guard against this to avoid creating two 275// copies of the text that defines the reflected class resultArray4_int. 276 277#pragma rs reduce(sillySumIntoDecArray) accumulator(aiAccum) outconverter(outSillySumIntoDecArray) 278static void outSillySumIntoDecArray(int (*out)[4], const int *accumDatum) { 279 for (int i = 0; i < 4; ++i) 280 (*out)[i] = (*accumDatum)/(i+1); 281} 282 283#pragma rs reduce(sillySumIntoIncArray) accumulator(aiAccum) outconverter(outSillySumIntoIncArray) 284static void outSillySumIntoIncArray(int (*out)[4], const int *accumDatum) { 285 for (int i = 0; i < 4; ++i) 286 (*out)[i] = (*accumDatum)/(4-i); 287} 288 289///////////////////////////////////////////////////////////////////////// 290 291// finds min values (not their locations) from matrix input 292 293// tests matrix input and matrix accumulator 294 295// also tests calling conventions for two different composite types 296// rs_matrix2x2: 32-bit coerces this to an int array 297// 64-bit coerces this to float array 298// rs_matrix4x4: 64-bit passes this by reference 299 300//....................................................................... 301 302#pragma rs reduce(findMinMat2) \ 303 initializer(fMinMat2Init) accumulator(fMinMat2Accumulator) \ 304 outconverter(fMinMat2OutConverter) 305 306static void fMinMat2Init(rs_matrix2x2 *accum) { 307 for (int i = 0; i < 2; ++i) 308 for (int j = 0; j < 2; ++j) 309 rsMatrixSet(accum, i, j, posInf); 310} 311 312static void fMinMat2Accumulator(rs_matrix2x2 *accum, rs_matrix2x2 val) { 313 for (int i = 0; i < 2; ++i) { 314 for (int j = 0; j < 2; ++j) { 315 const float accumElt = rsMatrixGet(accum, i, j); 316 const float valElt = rsMatrixGet(&val, i, j); 317 if (valElt < accumElt) 318 rsMatrixSet(accum, i, j, valElt); 319 } 320 } 321} 322 323// reduction does not support matrix result, so use array instead 324static void fMinMat2OutConverter(float (*result)[4], const rs_matrix2x2 *accum) { 325 for (int i = 0; i < 4; ++i) 326 (*result)[i] = accum->m[i]; 327} 328 329//....................................................................... 330 331#pragma rs reduce(findMinMat4) \ 332 initializer(fMinMat4Init) accumulator(fMinMat4Accumulator) \ 333 outconverter(fMinMat4OutConverter) 334 335static void fMinMat4Init(rs_matrix4x4 *accum) { 336 for (int i = 0; i < 4; ++i) 337 for (int j = 0; j < 4; ++j) 338 rsMatrixSet(accum, i, j, posInf); 339} 340 341static void fMinMat4Accumulator(rs_matrix4x4 *accum, rs_matrix4x4 val) { 342 for (int i = 0; i < 4; ++i) { 343 for (int j = 0; j < 4; ++j) { 344 const float accumElt = rsMatrixGet(accum, i, j); 345 const float valElt = rsMatrixGet(&val, i, j); 346 if (valElt < accumElt) 347 rsMatrixSet(accum, i, j, valElt); 348 } 349 } 350} 351 352// reduction does not support matrix result, so use array instead 353static void fMinMat4OutConverter(float (*result)[16], const rs_matrix4x4 *accum) { 354 for (int i = 0; i < 16; ++i) 355 (*result)[i] = accum->m[i]; 356} 357