xref: /aosp_15_r20/external/deqp/framework/common/tcuTexLookupVerifier.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Tester Core
3  * ----------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Texture lookup simulator that is capable of verifying generic
22  *          lookup results based on accuracy parameters.
23  *//*--------------------------------------------------------------------*/
24 
25 #include "tcuTexLookupVerifier.hpp"
26 #include "tcuTexVerifierUtil.hpp"
27 #include "tcuVectorUtil.hpp"
28 #include "tcuTextureUtil.hpp"
29 #include "deMath.h"
30 
31 namespace tcu
32 {
33 
34 using namespace TexVerifierUtil;
35 
36 // Generic utilities
37 
38 #if defined(DE_DEBUG)
isSamplerSupported(const Sampler & sampler)39 static bool isSamplerSupported(const Sampler &sampler)
40 {
41     return sampler.compare == Sampler::COMPAREMODE_NONE && isWrapModeSupported(sampler.wrapS) &&
42            isWrapModeSupported(sampler.wrapT) && isWrapModeSupported(sampler.wrapR);
43 }
44 #endif // DE_DEBUG
45 
46 // Color read & compare utilities
47 
coordsInBounds(const ConstPixelBufferAccess & access,int x,int y,int z)48 static inline bool coordsInBounds(const ConstPixelBufferAccess &access, int x, int y, int z)
49 {
50     return de::inBounds(x, 0, access.getWidth()) && de::inBounds(y, 0, access.getHeight()) &&
51            de::inBounds(z, 0, access.getDepth());
52 }
53 
54 template <typename ScalarType>
lookup(const ConstPixelBufferAccess & access,const Sampler & sampler,int i,int j,int k)55 inline Vector<ScalarType, 4> lookup(const ConstPixelBufferAccess &access, const Sampler &sampler, int i, int j, int k)
56 {
57     if (coordsInBounds(access, i, j, k))
58         return access.getPixelT<ScalarType>(i, j, k);
59     else
60         return sampleTextureBorder<ScalarType>(access.getFormat(), sampler);
61 }
62 
63 template <>
lookup(const ConstPixelBufferAccess & access,const Sampler & sampler,int i,int j,int k)64 inline Vector<float, 4> lookup(const ConstPixelBufferAccess &access, const Sampler &sampler, int i, int j, int k)
65 {
66     // Specialization for float lookups: sRGB conversion is performed as specified in format.
67     if (coordsInBounds(access, i, j, k))
68     {
69         const Vec4 p = access.getPixel(i, j, k);
70         return isSRGB(access.getFormat()) ? sRGBToLinear(p) : p;
71     }
72     else
73         return sampleTextureBorder<float>(access.getFormat(), sampler);
74 }
75 
isColorValid(const LookupPrecision & prec,const Vec4 & ref,const Vec4 & result)76 static inline bool isColorValid(const LookupPrecision &prec, const Vec4 &ref, const Vec4 &result)
77 {
78     const Vec4 diff = abs(ref - result);
79     return boolAll(logicalOr(lessThanEqual(diff, prec.colorThreshold), logicalNot(prec.colorMask)));
80 }
81 
isColorValid(const IntLookupPrecision & prec,const IVec4 & ref,const IVec4 & result)82 static inline bool isColorValid(const IntLookupPrecision &prec, const IVec4 &ref, const IVec4 &result)
83 {
84     return boolAll(
85         logicalOr(lessThanEqual(absDiff(ref, result).asUint(), prec.colorThreshold), logicalNot(prec.colorMask)));
86 }
87 
isColorValid(const IntLookupPrecision & prec,const UVec4 & ref,const UVec4 & result)88 static inline bool isColorValid(const IntLookupPrecision &prec, const UVec4 &ref, const UVec4 &result)
89 {
90     return boolAll(logicalOr(lessThanEqual(absDiff(ref, result), prec.colorThreshold), logicalNot(prec.colorMask)));
91 }
92 
93 struct ColorQuad
94 {
95     Vec4 p00; //!< (0, 0)
96     Vec4 p01; //!< (1, 0)
97     Vec4 p10; //!< (0, 1)
98     Vec4 p11; //!< (1, 1)
99 };
100 
lookupQuad(ColorQuad & dst,const ConstPixelBufferAccess & level,const Sampler & sampler,int x0,int x1,int y0,int y1,int z)101 static void lookupQuad(ColorQuad &dst, const ConstPixelBufferAccess &level, const Sampler &sampler, int x0, int x1,
102                        int y0, int y1, int z)
103 {
104     dst.p00 = lookup<float>(level, sampler, x0, y0, z);
105     dst.p10 = lookup<float>(level, sampler, x1, y0, z);
106     dst.p01 = lookup<float>(level, sampler, x0, y1, z);
107     dst.p11 = lookup<float>(level, sampler, x1, y1, z);
108 }
109 
110 struct ColorLine
111 {
112     Vec4 p0; //!< 0
113     Vec4 p1; //!< 1
114 };
115 
lookupLine(ColorLine & dst,const ConstPixelBufferAccess & level,const Sampler & sampler,int x0,int x1,int y)116 static void lookupLine(ColorLine &dst, const ConstPixelBufferAccess &level, const Sampler &sampler, int x0, int x1,
117                        int y)
118 {
119     dst.p0 = lookup<float>(level, sampler, x0, y, 0);
120     dst.p1 = lookup<float>(level, sampler, x1, y, 0);
121 }
122 
123 template <typename T, int Size>
minComp(const Vector<T,Size> & vec)124 static T minComp(const Vector<T, Size> &vec)
125 {
126     T minVal = vec[0];
127     for (int ndx = 1; ndx < Size; ndx++)
128         minVal = de::min(minVal, vec[ndx]);
129     return minVal;
130 }
131 
132 template <typename T, int Size>
maxComp(const Vector<T,Size> & vec)133 static T maxComp(const Vector<T, Size> &vec)
134 {
135     T maxVal = vec[0];
136     for (int ndx = 1; ndx < Size; ndx++)
137         maxVal = de::max(maxVal, vec[ndx]);
138     return maxVal;
139 }
140 
computeBilinearSearchStepFromFloatLine(const LookupPrecision & prec,const ColorLine & line)141 static float computeBilinearSearchStepFromFloatLine(const LookupPrecision &prec, const ColorLine &line)
142 {
143     DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
144 
145     const int maxSteps   = 1 << 16;
146     const Vec4 d         = abs(line.p1 - line.p0);
147     const Vec4 stepCount = d / prec.colorThreshold;
148     const Vec4 minStep   = 1.0f / (stepCount + 1.0f);
149     const float step     = de::max(minComp(minStep), 1.0f / float(maxSteps));
150 
151     return step;
152 }
153 
computeBilinearSearchStepFromFloatQuad(const LookupPrecision & prec,const ColorQuad & quad)154 static float computeBilinearSearchStepFromFloatQuad(const LookupPrecision &prec, const ColorQuad &quad)
155 {
156     DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
157 
158     const int maxSteps   = 1 << 16;
159     const Vec4 d0        = abs(quad.p10 - quad.p00);
160     const Vec4 d1        = abs(quad.p01 - quad.p00);
161     const Vec4 d2        = abs(quad.p11 - quad.p10);
162     const Vec4 d3        = abs(quad.p11 - quad.p01);
163     const Vec4 maxD      = max(d0, max(d1, max(d2, d3)));
164     const Vec4 stepCount = maxD / prec.colorThreshold;
165     const Vec4 minStep   = 1.0f / (stepCount + 1.0f);
166     const float step     = de::max(minComp(minStep), 1.0f / float(maxSteps));
167 
168     return step;
169 }
170 
computeBilinearSearchStepForUnorm(const LookupPrecision & prec)171 static float computeBilinearSearchStepForUnorm(const LookupPrecision &prec)
172 {
173     DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
174 
175     const Vec4 stepCount = 1.0f / prec.colorThreshold;
176     const Vec4 minStep   = 1.0f / (stepCount + 1.0f);
177     const float step     = minComp(minStep);
178 
179     return step;
180 }
181 
computeBilinearSearchStepForSnorm(const LookupPrecision & prec)182 static float computeBilinearSearchStepForSnorm(const LookupPrecision &prec)
183 {
184     DE_ASSERT(boolAll(greaterThan(prec.colorThreshold, Vec4(0.0f))));
185 
186     const Vec4 stepCount = 2.0f / prec.colorThreshold;
187     const Vec4 minStep   = 1.0f / (stepCount + 1.0f);
188     const float step     = minComp(minStep);
189 
190     return step;
191 }
192 
min(const ColorLine & line)193 static inline Vec4 min(const ColorLine &line)
194 {
195     return min(line.p0, line.p1);
196 }
197 
max(const ColorLine & line)198 static inline Vec4 max(const ColorLine &line)
199 {
200     return max(line.p0, line.p1);
201 }
202 
min(const ColorQuad & quad)203 static inline Vec4 min(const ColorQuad &quad)
204 {
205     return min(quad.p00, min(quad.p10, min(quad.p01, quad.p11)));
206 }
207 
max(const ColorQuad & quad)208 static inline Vec4 max(const ColorQuad &quad)
209 {
210     return max(quad.p00, max(quad.p10, max(quad.p01, quad.p11)));
211 }
212 
isInColorBounds(const LookupPrecision & prec,const ColorQuad & quad,const Vec4 & result)213 static bool isInColorBounds(const LookupPrecision &prec, const ColorQuad &quad, const Vec4 &result)
214 {
215     const tcu::Vec4 minVal = min(quad) - prec.colorThreshold;
216     const tcu::Vec4 maxVal = max(quad) + prec.colorThreshold;
217     return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)),
218                              logicalNot(prec.colorMask)));
219 }
220 
isInColorBounds(const LookupPrecision & prec,const ColorQuad & quad0,const ColorQuad & quad1,const Vec4 & result)221 static bool isInColorBounds(const LookupPrecision &prec, const ColorQuad &quad0, const ColorQuad &quad1,
222                             const Vec4 &result)
223 {
224     const tcu::Vec4 minVal = min(min(quad0), min(quad1)) - prec.colorThreshold;
225     const tcu::Vec4 maxVal = max(max(quad0), max(quad1)) + prec.colorThreshold;
226     return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)),
227                              logicalNot(prec.colorMask)));
228 }
229 
isInColorBounds(const LookupPrecision & prec,const ColorLine & line0,const ColorLine & line1,const Vec4 & result)230 static bool isInColorBounds(const LookupPrecision &prec, const ColorLine &line0, const ColorLine &line1,
231                             const Vec4 &result)
232 {
233     const tcu::Vec4 minVal = min(min(line0), min(line1)) - prec.colorThreshold;
234     const tcu::Vec4 maxVal = max(max(line0), max(line1)) + prec.colorThreshold;
235     return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)),
236                              logicalNot(prec.colorMask)));
237 }
238 
isInColorBounds(const LookupPrecision & prec,const ColorQuad & quad00,const ColorQuad & quad01,const ColorQuad & quad10,const ColorQuad & quad11,const Vec4 & result)239 static bool isInColorBounds(const LookupPrecision &prec, const ColorQuad &quad00, const ColorQuad &quad01,
240                             const ColorQuad &quad10, const ColorQuad &quad11, const Vec4 &result)
241 {
242     const tcu::Vec4 minVal = min(min(quad00), min(min(quad01), min(min(quad10), min(quad11)))) - prec.colorThreshold;
243     const tcu::Vec4 maxVal = max(max(quad00), max(max(quad01), max(max(quad10), max(quad11)))) + prec.colorThreshold;
244     return boolAll(logicalOr(logicalAnd(greaterThanEqual(result, minVal), lessThanEqual(result, maxVal)),
245                              logicalNot(prec.colorMask)));
246 }
247 
248 // Range search utilities
249 
isLinearRangeValid(const LookupPrecision & prec,const Vec4 & c0,const Vec4 & c1,const Vec2 & fBounds,const Vec4 & result)250 static bool isLinearRangeValid(const LookupPrecision &prec, const Vec4 &c0, const Vec4 &c1, const Vec2 &fBounds,
251                                const Vec4 &result)
252 {
253     // This is basically line segment - AABB test. Valid interpolation line is checked
254     // against result AABB constructed by applying threshold.
255 
256     const Vec4 i0     = c0 * (1.0f - fBounds[0]) + c1 * fBounds[0];
257     const Vec4 i1     = c0 * (1.0f - fBounds[1]) + c1 * fBounds[1];
258     const Vec4 rMin   = result - prec.colorThreshold;
259     const Vec4 rMax   = result + prec.colorThreshold;
260     bool allIntersect = true;
261 
262     // Algorithm: For each component check whether segment endpoints are inside, or intersect with slab.
263     // If all intersect or are inside, line segment intersects the whole 4D AABB.
264     for (int compNdx = 0; compNdx < 4; compNdx++)
265     {
266         if (!prec.colorMask[compNdx])
267             continue;
268 
269         // Signs for both bounds: false = left, true = right.
270         const bool sMin0 = i0[compNdx] >= rMin[compNdx];
271         const bool sMin1 = i1[compNdx] >= rMin[compNdx];
272         const bool sMax0 = i0[compNdx] > rMax[compNdx];
273         const bool sMax1 = i1[compNdx] > rMax[compNdx];
274 
275         // If all signs are equal, line segment is outside bounds.
276         if (sMin0 == sMin1 && sMin1 == sMax0 && sMax0 == sMax1)
277         {
278             allIntersect = false;
279             break;
280         }
281     }
282 
283     return allIntersect;
284 }
285 
isBilinearRangeValid(const LookupPrecision & prec,const ColorQuad & quad,const Vec2 & xBounds,const Vec2 & yBounds,const float searchStep,const Vec4 & result)286 static bool isBilinearRangeValid(const LookupPrecision &prec, const ColorQuad &quad, const Vec2 &xBounds,
287                                  const Vec2 &yBounds, const float searchStep, const Vec4 &result)
288 {
289     DE_ASSERT(xBounds.x() <= xBounds.y());
290     DE_ASSERT(yBounds.x() <= yBounds.y());
291     DE_ASSERT(xBounds.x() + searchStep > xBounds.x()); // step is not effectively 0
292     DE_ASSERT(xBounds.y() + searchStep > xBounds.y());
293 
294     if (!isInColorBounds(prec, quad, result))
295         return false;
296 
297     for (float x = xBounds.x(); x < xBounds.y() + searchStep; x += searchStep)
298     {
299         const float a = de::min(x, xBounds.y());
300         const Vec4 c0 = quad.p00 * (1.0f - a) + quad.p10 * a;
301         const Vec4 c1 = quad.p01 * (1.0f - a) + quad.p11 * a;
302 
303         if (isLinearRangeValid(prec, c0, c1, yBounds, result))
304             return true;
305     }
306 
307     return false;
308 }
309 
isTrilinearRangeValid(const LookupPrecision & prec,const ColorQuad & quad0,const ColorQuad & quad1,const Vec2 & xBounds,const Vec2 & yBounds,const Vec2 & zBounds,const float searchStep,const Vec4 & result)310 static bool isTrilinearRangeValid(const LookupPrecision &prec, const ColorQuad &quad0, const ColorQuad &quad1,
311                                   const Vec2 &xBounds, const Vec2 &yBounds, const Vec2 &zBounds, const float searchStep,
312                                   const Vec4 &result)
313 {
314     DE_ASSERT(xBounds.x() <= xBounds.y());
315     DE_ASSERT(yBounds.x() <= yBounds.y());
316     DE_ASSERT(zBounds.x() <= zBounds.y());
317     DE_ASSERT(xBounds.x() + searchStep > xBounds.x()); // step is not effectively 0
318     DE_ASSERT(xBounds.y() + searchStep > xBounds.y());
319     DE_ASSERT(yBounds.x() + searchStep > yBounds.x());
320     DE_ASSERT(yBounds.y() + searchStep > yBounds.y());
321 
322     if (!isInColorBounds(prec, quad0, quad1, result))
323         return false;
324 
325     for (float x = xBounds.x(); x < xBounds.y() + searchStep; x += searchStep)
326     {
327         for (float y = yBounds.x(); y < yBounds.y() + searchStep; y += searchStep)
328         {
329             const float a = de::min(x, xBounds.y());
330             const float b = de::min(y, yBounds.y());
331             const Vec4 c0 = quad0.p00 * (1.0f - a) * (1.0f - b) + quad0.p10 * a * (1.0f - b) +
332                             quad0.p01 * (1.0f - a) * b + quad0.p11 * a * b;
333             const Vec4 c1 = quad1.p00 * (1.0f - a) * (1.0f - b) + quad1.p10 * a * (1.0f - b) +
334                             quad1.p01 * (1.0f - a) * b + quad1.p11 * a * b;
335 
336             if (isLinearRangeValid(prec, c0, c1, zBounds, result))
337                 return true;
338         }
339     }
340 
341     return false;
342 }
343 
isReductionValid(const LookupPrecision & prec,const Vec4 & c0,const Vec4 & c1,tcu::Sampler::ReductionMode reductionMode,const Vec4 & result)344 static bool isReductionValid(const LookupPrecision &prec, const Vec4 &c0, const Vec4 &c1,
345                              tcu::Sampler::ReductionMode reductionMode, const Vec4 &result)
346 {
347     DE_ASSERT(reductionMode == tcu::Sampler::MIN || reductionMode == tcu::Sampler::MAX);
348 
349     const Vec4 color = (reductionMode == tcu::Sampler::MIN ? tcu::min(c0, c1) : tcu::max(c0, c1));
350 
351     return isColorValid(prec, color, result);
352 }
353 
isReductionValid(const LookupPrecision & prec,const ColorQuad & quad,tcu::Sampler::ReductionMode reductionMode,const Vec4 & result)354 static bool isReductionValid(const LookupPrecision &prec, const ColorQuad &quad,
355                              tcu::Sampler::ReductionMode reductionMode, const Vec4 &result)
356 {
357     DE_ASSERT(reductionMode == tcu::Sampler::MIN || reductionMode == tcu::Sampler::MAX);
358 
359     const Vec4 c0 = (reductionMode == tcu::Sampler::MIN ? tcu::min(quad.p00, quad.p01) : tcu::max(quad.p00, quad.p01));
360     const Vec4 c1 = (reductionMode == tcu::Sampler::MIN ? tcu::min(quad.p10, quad.p11) : tcu::max(quad.p10, quad.p11));
361 
362     return isReductionValid(prec, c0, c1, reductionMode, result);
363 }
364 
isReductionValid(const LookupPrecision & prec,const ColorQuad & quad0,const ColorQuad & quad1,tcu::Sampler::ReductionMode reductionMode,const Vec4 & result)365 static bool isReductionValid(const LookupPrecision &prec, const ColorQuad &quad0, const ColorQuad &quad1,
366                              tcu::Sampler::ReductionMode reductionMode, const Vec4 &result)
367 {
368     DE_ASSERT(reductionMode == tcu::Sampler::MIN || reductionMode == tcu::Sampler::MAX);
369 
370     const ColorQuad quad = {
371         reductionMode == tcu::Sampler::MIN ? tcu::min(quad0.p00, quad1.p00) : tcu::max(quad0.p00, quad1.p00), // p00
372         reductionMode == tcu::Sampler::MIN ? tcu::min(quad0.p01, quad1.p01) : tcu::max(quad0.p01, quad1.p01), // p01
373         reductionMode == tcu::Sampler::MIN ? tcu::min(quad0.p10, quad1.p10) : tcu::max(quad0.p10, quad1.p10), // p10
374         reductionMode == tcu::Sampler::MIN ? tcu::min(quad0.p11, quad1.p11) : tcu::max(quad0.p11, quad1.p11), // p11
375     };
376 
377     return isReductionValid(prec, quad, reductionMode, result);
378 }
379 
is1DTrilinearFilterResultValid(const LookupPrecision & prec,const ColorLine & line0,const ColorLine & line1,const Vec2 & xBounds0,const Vec2 & xBounds1,const Vec2 & zBounds,const float searchStep,const Vec4 & result)380 static bool is1DTrilinearFilterResultValid(const LookupPrecision &prec, const ColorLine &line0, const ColorLine &line1,
381                                            const Vec2 &xBounds0, const Vec2 &xBounds1, const Vec2 &zBounds,
382                                            const float searchStep, const Vec4 &result)
383 {
384     DE_ASSERT(xBounds0.x() <= xBounds0.y());
385     DE_ASSERT(xBounds1.x() <= xBounds1.y());
386     DE_ASSERT(xBounds0.x() + searchStep > xBounds0.x()); // step is not effectively 0
387     DE_ASSERT(xBounds0.y() + searchStep > xBounds0.y());
388     DE_ASSERT(xBounds1.x() + searchStep > xBounds1.x());
389     DE_ASSERT(xBounds1.y() + searchStep > xBounds1.y());
390 
391     if (!isInColorBounds(prec, line0, line1, result))
392         return false;
393 
394     for (float x0 = xBounds0.x(); x0 < xBounds0.y() + searchStep; x0 += searchStep)
395     {
396         const float a0 = de::min(x0, xBounds0.y());
397         const Vec4 c0  = line0.p0 * (1.0f - a0) + line0.p1 * a0;
398 
399         for (float x1 = xBounds1.x(); x1 <= xBounds1.y(); x1 += searchStep)
400         {
401             const float a1 = de::min(x1, xBounds1.y());
402             const Vec4 c1  = line1.p0 * (1.0f - a1) + line1.p1 * a1;
403 
404             if (isLinearRangeValid(prec, c0, c1, zBounds, result))
405                 return true;
406         }
407     }
408 
409     return false;
410 }
411 
is2DTrilinearFilterResultValid(const LookupPrecision & prec,const ColorQuad & quad0,const ColorQuad & quad1,const Vec2 & xBounds0,const Vec2 & yBounds0,const Vec2 & xBounds1,const Vec2 & yBounds1,const Vec2 & zBounds,const float searchStep,const Vec4 & result)412 static bool is2DTrilinearFilterResultValid(const LookupPrecision &prec, const ColorQuad &quad0, const ColorQuad &quad1,
413                                            const Vec2 &xBounds0, const Vec2 &yBounds0, const Vec2 &xBounds1,
414                                            const Vec2 &yBounds1, const Vec2 &zBounds, const float searchStep,
415                                            const Vec4 &result)
416 {
417     DE_ASSERT(xBounds0.x() <= xBounds0.y());
418     DE_ASSERT(yBounds0.x() <= yBounds0.y());
419     DE_ASSERT(xBounds1.x() <= xBounds1.y());
420     DE_ASSERT(yBounds1.x() <= yBounds1.y());
421     DE_ASSERT(xBounds0.x() + searchStep > xBounds0.x()); // step is not effectively 0
422     DE_ASSERT(xBounds0.y() + searchStep > xBounds0.y());
423     DE_ASSERT(yBounds0.x() + searchStep > yBounds0.x());
424     DE_ASSERT(yBounds0.y() + searchStep > yBounds0.y());
425     DE_ASSERT(xBounds1.x() + searchStep > xBounds1.x());
426     DE_ASSERT(xBounds1.y() + searchStep > xBounds1.y());
427     DE_ASSERT(yBounds1.x() + searchStep > yBounds1.x());
428     DE_ASSERT(yBounds1.y() + searchStep > yBounds1.y());
429 
430     if (!isInColorBounds(prec, quad0, quad1, result))
431         return false;
432 
433     for (float x0 = xBounds0.x(); x0 < xBounds0.y() + searchStep; x0 += searchStep)
434     {
435         for (float y0 = yBounds0.x(); y0 < yBounds0.y() + searchStep; y0 += searchStep)
436         {
437             const float a0 = de::min(x0, xBounds0.y());
438             const float b0 = de::min(y0, yBounds0.y());
439             const Vec4 c0  = quad0.p00 * (1.0f - a0) * (1.0f - b0) + quad0.p10 * a0 * (1.0f - b0) +
440                             quad0.p01 * (1.0f - a0) * b0 + quad0.p11 * a0 * b0;
441 
442             for (float x1 = xBounds1.x(); x1 <= xBounds1.y(); x1 += searchStep)
443             {
444                 for (float y1 = yBounds1.x(); y1 <= yBounds1.y(); y1 += searchStep)
445                 {
446                     const float a1 = de::min(x1, xBounds1.y());
447                     const float b1 = de::min(y1, yBounds1.y());
448                     const Vec4 c1  = quad1.p00 * (1.0f - a1) * (1.0f - b1) + quad1.p10 * a1 * (1.0f - b1) +
449                                     quad1.p01 * (1.0f - a1) * b1 + quad1.p11 * a1 * b1;
450 
451                     if (isLinearRangeValid(prec, c0, c1, zBounds, result))
452                         return true;
453                 }
454             }
455         }
456     }
457 
458     return false;
459 }
460 
is3DTrilinearFilterResultValid(const LookupPrecision & prec,const ColorQuad & quad00,const ColorQuad & quad01,const ColorQuad & quad10,const ColorQuad & quad11,const Vec2 & xBounds0,const Vec2 & yBounds0,const Vec2 & zBounds0,const Vec2 & xBounds1,const Vec2 & yBounds1,const Vec2 & zBounds1,const Vec2 & wBounds,const float searchStep,const Vec4 & result)461 static bool is3DTrilinearFilterResultValid(const LookupPrecision &prec, const ColorQuad &quad00,
462                                            const ColorQuad &quad01, const ColorQuad &quad10, const ColorQuad &quad11,
463                                            const Vec2 &xBounds0, const Vec2 &yBounds0, const Vec2 &zBounds0,
464                                            const Vec2 &xBounds1, const Vec2 &yBounds1, const Vec2 &zBounds1,
465                                            const Vec2 &wBounds, const float searchStep, const Vec4 &result)
466 {
467     DE_ASSERT(xBounds0.x() <= xBounds0.y());
468     DE_ASSERT(yBounds0.x() <= yBounds0.y());
469     DE_ASSERT(zBounds0.x() <= zBounds0.y());
470     DE_ASSERT(xBounds1.x() <= xBounds1.y());
471     DE_ASSERT(yBounds1.x() <= yBounds1.y());
472     DE_ASSERT(zBounds1.x() <= zBounds1.y());
473     DE_ASSERT(xBounds0.x() + searchStep > xBounds0.x()); // step is not effectively 0
474     DE_ASSERT(xBounds0.y() + searchStep > xBounds0.y());
475     DE_ASSERT(yBounds0.x() + searchStep > yBounds0.x());
476     DE_ASSERT(yBounds0.y() + searchStep > yBounds0.y());
477     DE_ASSERT(zBounds0.x() + searchStep > zBounds0.x());
478     DE_ASSERT(zBounds0.y() + searchStep > zBounds0.y());
479     DE_ASSERT(xBounds1.x() + searchStep > xBounds1.x());
480     DE_ASSERT(xBounds1.y() + searchStep > xBounds1.y());
481     DE_ASSERT(yBounds1.x() + searchStep > yBounds1.x());
482     DE_ASSERT(yBounds1.y() + searchStep > yBounds1.y());
483     DE_ASSERT(zBounds1.x() + searchStep > zBounds1.x());
484     DE_ASSERT(zBounds1.y() + searchStep > zBounds1.y());
485 
486     if (!isInColorBounds(prec, quad00, quad01, quad10, quad11, result))
487         return false;
488 
489     for (float x0 = xBounds0.x(); x0 < xBounds0.y() + searchStep; x0 += searchStep)
490     {
491         for (float y0 = yBounds0.x(); y0 < yBounds0.y() + searchStep; y0 += searchStep)
492         {
493             const float a0 = de::min(x0, xBounds0.y());
494             const float b0 = de::min(y0, yBounds0.y());
495             const Vec4 c00 = quad00.p00 * (1.0f - a0) * (1.0f - b0) + quad00.p10 * a0 * (1.0f - b0) +
496                              quad00.p01 * (1.0f - a0) * b0 + quad00.p11 * a0 * b0;
497             const Vec4 c01 = quad01.p00 * (1.0f - a0) * (1.0f - b0) + quad01.p10 * a0 * (1.0f - b0) +
498                              quad01.p01 * (1.0f - a0) * b0 + quad01.p11 * a0 * b0;
499 
500             for (float z0 = zBounds0.x(); z0 < zBounds0.y() + searchStep; z0 += searchStep)
501             {
502                 const float c0 = de::min(z0, zBounds0.y());
503                 const Vec4 cz0 = c00 * (1.0f - c0) + c01 * c0;
504 
505                 for (float x1 = xBounds1.x(); x1 < xBounds1.y() + searchStep; x1 += searchStep)
506                 {
507                     for (float y1 = yBounds1.x(); y1 < yBounds1.y() + searchStep; y1 += searchStep)
508                     {
509                         const float a1 = de::min(x1, xBounds1.y());
510                         const float b1 = de::min(y1, yBounds1.y());
511                         const Vec4 c10 = quad10.p00 * (1.0f - a1) * (1.0f - b1) + quad10.p10 * a1 * (1.0f - b1) +
512                                          quad10.p01 * (1.0f - a1) * b1 + quad10.p11 * a1 * b1;
513                         const Vec4 c11 = quad11.p00 * (1.0f - a1) * (1.0f - b1) + quad11.p10 * a1 * (1.0f - b1) +
514                                          quad11.p01 * (1.0f - a1) * b1 + quad11.p11 * a1 * b1;
515 
516                         for (float z1 = zBounds1.x(); z1 < zBounds1.y() + searchStep; z1 += searchStep)
517                         {
518                             const float c1 = de::min(z1, zBounds1.y());
519                             const Vec4 cz1 = c10 * (1.0f - c1) + c11 * c1;
520 
521                             if (isLinearRangeValid(prec, cz0, cz1, wBounds, result))
522                                 return true;
523                         }
524                     }
525                 }
526             }
527         }
528     }
529 
530     return false;
531 }
532 
533 template <typename PrecType, typename ScalarType>
isNearestSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const float coordX,const int coordY,const Vector<ScalarType,4> & result)534 static bool isNearestSampleResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
535                                        const PrecType &prec, const float coordX, const int coordY,
536                                        const Vector<ScalarType, 4> &result)
537 {
538     DE_ASSERT(level.getDepth() == 1);
539 
540     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coordX,
541                                                          prec.coordBits.x(), prec.uvwBits.x());
542 
543     const int minI = deFloorFloatToInt32(uBounds.x());
544     const int maxI = deFloorFloatToInt32(uBounds.y());
545 
546     for (int i = minI; i <= maxI; i++)
547     {
548         const int x                       = wrap(sampler.wrapS, i, level.getWidth());
549         const Vector<ScalarType, 4> color = lookup<ScalarType>(level, sampler, x, coordY, 0);
550 
551         if (isColorValid(prec, color, result))
552             return true;
553     }
554 
555     return false;
556 }
557 
558 template <typename PrecType, typename ScalarType>
isNearestSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const Vec2 & coord,const int coordZ,const Vector<ScalarType,4> & result)559 static bool isNearestSampleResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
560                                        const PrecType &prec, const Vec2 &coord, const int coordZ,
561                                        const Vector<ScalarType, 4> &result)
562 {
563     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(),
564                                                          prec.coordBits.x(), prec.uvwBits.x());
565     const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(),
566                                                          prec.coordBits.y(), prec.uvwBits.y());
567 
568     // Integer coordinates - without wrap mode
569     const int minI = deFloorFloatToInt32(uBounds.x());
570     const int maxI = deFloorFloatToInt32(uBounds.y());
571     const int minJ = deFloorFloatToInt32(vBounds.x());
572     const int maxJ = deFloorFloatToInt32(vBounds.y());
573 
574     // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
575 
576     for (int j = minJ; j <= maxJ; j++)
577     {
578         for (int i = minI; i <= maxI; i++)
579         {
580             const int x                       = wrap(sampler.wrapS, i, level.getWidth());
581             const int y                       = wrap(sampler.wrapT, j, level.getHeight());
582             const Vector<ScalarType, 4> color = lookup<ScalarType>(level, sampler, x, y, coordZ);
583 
584             if (isColorValid(prec, color, result))
585                 return true;
586         }
587     }
588 
589     return false;
590 }
591 
592 template <typename PrecType, typename ScalarType>
isNearestSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const Vec3 & coord,const Vector<ScalarType,4> & result)593 static bool isNearestSampleResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
594                                        const PrecType &prec, const Vec3 &coord, const Vector<ScalarType, 4> &result)
595 {
596     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(),
597                                                          prec.coordBits.x(), prec.uvwBits.x());
598     const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(),
599                                                          prec.coordBits.y(), prec.uvwBits.y());
600     const Vec2 wBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getDepth(), coord.z(),
601                                                          prec.coordBits.z(), prec.uvwBits.z());
602 
603     // Integer coordinates - without wrap mode
604     const int minI = deFloorFloatToInt32(uBounds.x());
605     const int maxI = deFloorFloatToInt32(uBounds.y());
606     const int minJ = deFloorFloatToInt32(vBounds.x());
607     const int maxJ = deFloorFloatToInt32(vBounds.y());
608     const int minK = deFloorFloatToInt32(wBounds.x());
609     const int maxK = deFloorFloatToInt32(wBounds.y());
610 
611     // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
612 
613     for (int k = minK; k <= maxK; k++)
614     {
615         for (int j = minJ; j <= maxJ; j++)
616         {
617             for (int i = minI; i <= maxI; i++)
618             {
619                 const int x                       = wrap(sampler.wrapS, i, level.getWidth());
620                 const int y                       = wrap(sampler.wrapT, j, level.getHeight());
621                 const int z                       = wrap(sampler.wrapR, k, level.getDepth());
622                 const Vector<ScalarType, 4> color = lookup<ScalarType>(level, sampler, x, y, z);
623 
624                 if (isColorValid(prec, color, result))
625                     return true;
626             }
627         }
628     }
629 
630     return false;
631 }
632 
isLinearSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const LookupPrecision & prec,const float coordX,const int coordY,const Vec4 & result)633 bool isLinearSampleResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler, const LookupPrecision &prec,
634                                const float coordX, const int coordY, const Vec4 &result)
635 {
636     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coordX,
637                                                          prec.coordBits.x(), prec.uvwBits.x());
638 
639     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
640     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
641 
642     const int w = level.getWidth();
643 
644     const TextureFormat format         = level.getFormat();
645     const TextureChannelClass texClass = getTextureChannelClass(format.type);
646 
647     DE_ASSERT(texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
648               texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || texClass == TEXTURECHANNELCLASS_FLOATING_POINT ||
649               sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
650 
651     DE_UNREF(texClass);
652     DE_UNREF(format);
653 
654     for (int i = minI; i <= maxI; i++)
655     {
656         // Wrapped coordinates
657         const int x0 = wrap(sampler.wrapS, i, w);
658         const int x1 = wrap(sampler.wrapS, i + 1, w);
659 
660         // Bounds for filtering factors
661         const float minA = de::clamp((uBounds.x() - 0.5f) - float(i), 0.0f, 1.0f);
662         const float maxA = de::clamp((uBounds.y() - 0.5f) - float(i), 0.0f, 1.0f);
663 
664         const Vec4 colorA = lookup<float>(level, sampler, x0, coordY, 0);
665         const Vec4 colorB = lookup<float>(level, sampler, x1, coordY, 0);
666 
667         if (sampler.reductionMode == Sampler::WEIGHTED_AVERAGE)
668         {
669             if (isLinearRangeValid(prec, colorA, colorB, Vec2(minA, maxA), result))
670                 return true;
671         }
672         else
673         {
674             if (isReductionValid(prec, colorA, colorB, sampler.reductionMode, result))
675                 return true;
676         }
677     }
678 
679     return false;
680 }
681 
isLinearSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec4 & result)682 bool isLinearSampleResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler, const LookupPrecision &prec,
683                                const Vec2 &coord, const int coordZ, const Vec4 &result)
684 {
685     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(),
686                                                          prec.coordBits.x(), prec.uvwBits.x());
687     const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(),
688                                                          prec.coordBits.y(), prec.uvwBits.y());
689 
690     // Integer coordinate bounds for (x0,y0) - without wrap mode
691     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
692     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
693     const int minJ = deFloorFloatToInt32(vBounds.x() - 0.5f);
694     const int maxJ = deFloorFloatToInt32(vBounds.y() - 0.5f);
695 
696     const int w = level.getWidth();
697     const int h = level.getHeight();
698 
699     const TextureChannelClass texClass = getTextureChannelClass(level.getFormat().type);
700     float searchStep                   = texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ?
701                                              computeBilinearSearchStepForUnorm(prec) :
702                                          texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ?
703                                              computeBilinearSearchStepForSnorm(prec) :
704                                              0.0f; // Step is computed for floating-point quads based on texel values.
705 
706     DE_ASSERT(texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
707               texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || texClass == TEXTURECHANNELCLASS_FLOATING_POINT ||
708               sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
709 
710     // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
711 
712     for (int j = minJ; j <= maxJ; j++)
713     {
714         for (int i = minI; i <= maxI; i++)
715         {
716             // Wrapped coordinates
717             const int x0 = wrap(sampler.wrapS, i, w);
718             const int x1 = wrap(sampler.wrapS, i + 1, w);
719             const int y0 = wrap(sampler.wrapT, j, h);
720             const int y1 = wrap(sampler.wrapT, j + 1, h);
721 
722             // Bounds for filtering factors
723             const float minA = de::clamp((uBounds.x() - 0.5f) - float(i), 0.0f, 1.0f);
724             const float maxA = de::clamp((uBounds.y() - 0.5f) - float(i), 0.0f, 1.0f);
725             const float minB = de::clamp((vBounds.x() - 0.5f) - float(j), 0.0f, 1.0f);
726             const float maxB = de::clamp((vBounds.y() - 0.5f) - float(j), 0.0f, 1.0f);
727 
728             ColorQuad quad;
729             lookupQuad(quad, level, sampler, x0, x1, y0, y1, coordZ);
730 
731             if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
732                 searchStep = computeBilinearSearchStepFromFloatQuad(prec, quad);
733 
734             if (sampler.reductionMode == Sampler::WEIGHTED_AVERAGE)
735             {
736                 if (isBilinearRangeValid(prec, quad, Vec2(minA, maxA), Vec2(minB, maxB), searchStep, result))
737                     return true;
738             }
739             else
740             {
741                 if (isReductionValid(prec, quad, sampler.reductionMode, result))
742                     return true;
743             }
744         }
745     }
746 
747     return false;
748 }
749 
isLinearSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec4 & result)750 static bool isLinearSampleResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
751                                       const LookupPrecision &prec, const Vec3 &coord, const Vec4 &result)
752 {
753     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(),
754                                                          prec.coordBits.x(), prec.uvwBits.x());
755     const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(),
756                                                          prec.coordBits.y(), prec.uvwBits.y());
757     const Vec2 wBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getDepth(), coord.z(),
758                                                          prec.coordBits.z(), prec.uvwBits.z());
759 
760     // Integer coordinate bounds for (x0,y0) - without wrap mode
761     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
762     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
763     const int minJ = deFloorFloatToInt32(vBounds.x() - 0.5f);
764     const int maxJ = deFloorFloatToInt32(vBounds.y() - 0.5f);
765     const int minK = deFloorFloatToInt32(wBounds.x() - 0.5f);
766     const int maxK = deFloorFloatToInt32(wBounds.y() - 0.5f);
767 
768     const int w = level.getWidth();
769     const int h = level.getHeight();
770     const int d = level.getDepth();
771 
772     const TextureChannelClass texClass = getTextureChannelClass(level.getFormat().type);
773     float searchStep                   = texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ?
774                                              computeBilinearSearchStepForUnorm(prec) :
775                                          texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ?
776                                              computeBilinearSearchStepForSnorm(prec) :
777                                              0.0f; // Step is computed for floating-point quads based on texel values.
778 
779     DE_ASSERT(texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
780               texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || texClass == TEXTURECHANNELCLASS_FLOATING_POINT ||
781               sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
782 
783     // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
784 
785     for (int k = minK; k <= maxK; k++)
786     {
787         for (int j = minJ; j <= maxJ; j++)
788         {
789             for (int i = minI; i <= maxI; i++)
790             {
791                 // Wrapped coordinates
792                 const int x0 = wrap(sampler.wrapS, i, w);
793                 const int x1 = wrap(sampler.wrapS, i + 1, w);
794                 const int y0 = wrap(sampler.wrapT, j, h);
795                 const int y1 = wrap(sampler.wrapT, j + 1, h);
796                 const int z0 = wrap(sampler.wrapR, k, d);
797                 const int z1 = wrap(sampler.wrapR, k + 1, d);
798 
799                 // Bounds for filtering factors
800                 const float minA = de::clamp((uBounds.x() - 0.5f) - float(i), 0.0f, 1.0f);
801                 const float maxA = de::clamp((uBounds.y() - 0.5f) - float(i), 0.0f, 1.0f);
802                 const float minB = de::clamp((vBounds.x() - 0.5f) - float(j), 0.0f, 1.0f);
803                 const float maxB = de::clamp((vBounds.y() - 0.5f) - float(j), 0.0f, 1.0f);
804                 const float minC = de::clamp((wBounds.x() - 0.5f) - float(k), 0.0f, 1.0f);
805                 const float maxC = de::clamp((wBounds.y() - 0.5f) - float(k), 0.0f, 1.0f);
806 
807                 ColorQuad quad0, quad1;
808                 lookupQuad(quad0, level, sampler, x0, x1, y0, y1, z0);
809                 lookupQuad(quad1, level, sampler, x0, x1, y0, y1, z1);
810 
811                 if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
812                     searchStep = de::min(computeBilinearSearchStepFromFloatQuad(prec, quad0),
813                                          computeBilinearSearchStepFromFloatQuad(prec, quad1));
814 
815                 if (sampler.reductionMode == Sampler::WEIGHTED_AVERAGE)
816                 {
817                     if (isTrilinearRangeValid(prec, quad0, quad1, Vec2(minA, maxA), Vec2(minB, maxB), Vec2(minC, maxC),
818                                               searchStep, result))
819                         return true;
820                 }
821                 else
822                 {
823                     if (isReductionValid(prec, quad0, quad1, sampler.reductionMode, result))
824                         return true;
825                 }
826             }
827         }
828     }
829 
830     return false;
831 }
832 
isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const float coord,const int coordY,const Vec2 & fBounds,const Vec4 & result)833 static bool isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess &level0,
834                                                    const ConstPixelBufferAccess &level1, const Sampler &sampler,
835                                                    const LookupPrecision &prec, const float coord, const int coordY,
836                                                    const Vec2 &fBounds, const Vec4 &result)
837 {
838     const int w0 = level0.getWidth();
839     const int w1 = level1.getWidth();
840 
841     const Vec2 uBounds0 =
842         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord, prec.coordBits.x(), prec.uvwBits.x());
843     const Vec2 uBounds1 =
844         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord, prec.coordBits.x(), prec.uvwBits.x());
845 
846     // Integer coordinates - without wrap mode
847     const int minI0 = deFloorFloatToInt32(uBounds0.x());
848     const int maxI0 = deFloorFloatToInt32(uBounds0.y());
849     const int minI1 = deFloorFloatToInt32(uBounds1.x());
850     const int maxI1 = deFloorFloatToInt32(uBounds1.y());
851 
852     for (int i0 = minI0; i0 <= maxI0; i0++)
853     {
854         for (int i1 = minI1; i1 <= maxI1; i1++)
855         {
856             const Vec4 c0 = lookup<float>(level0, sampler, wrap(sampler.wrapS, i0, w0), coordY, 0);
857             const Vec4 c1 = lookup<float>(level1, sampler, wrap(sampler.wrapS, i1, w1), coordY, 0);
858 
859             if (isLinearRangeValid(prec, c0, c1, fBounds, result))
860                 return true;
861         }
862     }
863 
864     return false;
865 }
866 
isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const Vec4 & result)867 static bool isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess &level0,
868                                                    const ConstPixelBufferAccess &level1, const Sampler &sampler,
869                                                    const LookupPrecision &prec, const Vec2 &coord, const int coordZ,
870                                                    const Vec2 &fBounds, const Vec4 &result)
871 {
872     const int w0 = level0.getWidth();
873     const int w1 = level1.getWidth();
874     const int h0 = level0.getHeight();
875     const int h1 = level1.getHeight();
876 
877     const Vec2 uBounds0 =
878         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
879     const Vec2 uBounds1 =
880         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
881     const Vec2 vBounds0 =
882         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
883     const Vec2 vBounds1 =
884         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
885 
886     // Integer coordinates - without wrap mode
887     const int minI0 = deFloorFloatToInt32(uBounds0.x());
888     const int maxI0 = deFloorFloatToInt32(uBounds0.y());
889     const int minI1 = deFloorFloatToInt32(uBounds1.x());
890     const int maxI1 = deFloorFloatToInt32(uBounds1.y());
891     const int minJ0 = deFloorFloatToInt32(vBounds0.x());
892     const int maxJ0 = deFloorFloatToInt32(vBounds0.y());
893     const int minJ1 = deFloorFloatToInt32(vBounds1.x());
894     const int maxJ1 = deFloorFloatToInt32(vBounds1.y());
895 
896     for (int j0 = minJ0; j0 <= maxJ0; j0++)
897     {
898         for (int i0 = minI0; i0 <= maxI0; i0++)
899         {
900             for (int j1 = minJ1; j1 <= maxJ1; j1++)
901             {
902                 for (int i1 = minI1; i1 <= maxI1; i1++)
903                 {
904                     const Vec4 c0 = lookup<float>(level0, sampler, wrap(sampler.wrapS, i0, w0),
905                                                   wrap(sampler.wrapT, j0, h0), coordZ);
906                     const Vec4 c1 = lookup<float>(level1, sampler, wrap(sampler.wrapS, i1, w1),
907                                                   wrap(sampler.wrapT, j1, h1), coordZ);
908 
909                     if (isLinearRangeValid(prec, c0, c1, fBounds, result))
910                         return true;
911                 }
912             }
913         }
914     }
915 
916     return false;
917 }
918 
isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & fBounds,const Vec4 & result)919 static bool isNearestMipmapLinearSampleResultValid(const ConstPixelBufferAccess &level0,
920                                                    const ConstPixelBufferAccess &level1, const Sampler &sampler,
921                                                    const LookupPrecision &prec, const Vec3 &coord, const Vec2 &fBounds,
922                                                    const Vec4 &result)
923 {
924     const int w0 = level0.getWidth();
925     const int w1 = level1.getWidth();
926     const int h0 = level0.getHeight();
927     const int h1 = level1.getHeight();
928     const int d0 = level0.getDepth();
929     const int d1 = level1.getDepth();
930 
931     const Vec2 uBounds0 =
932         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
933     const Vec2 uBounds1 =
934         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
935     const Vec2 vBounds0 =
936         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
937     const Vec2 vBounds1 =
938         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
939     const Vec2 wBounds0 =
940         computeNonNormalizedCoordBounds(sampler.normalizedCoords, d0, coord.z(), prec.coordBits.z(), prec.uvwBits.z());
941     const Vec2 wBounds1 =
942         computeNonNormalizedCoordBounds(sampler.normalizedCoords, d1, coord.z(), prec.coordBits.z(), prec.uvwBits.z());
943 
944     // Integer coordinates - without wrap mode
945     const int minI0 = deFloorFloatToInt32(uBounds0.x());
946     const int maxI0 = deFloorFloatToInt32(uBounds0.y());
947     const int minI1 = deFloorFloatToInt32(uBounds1.x());
948     const int maxI1 = deFloorFloatToInt32(uBounds1.y());
949     const int minJ0 = deFloorFloatToInt32(vBounds0.x());
950     const int maxJ0 = deFloorFloatToInt32(vBounds0.y());
951     const int minJ1 = deFloorFloatToInt32(vBounds1.x());
952     const int maxJ1 = deFloorFloatToInt32(vBounds1.y());
953     const int minK0 = deFloorFloatToInt32(wBounds0.x());
954     const int maxK0 = deFloorFloatToInt32(wBounds0.y());
955     const int minK1 = deFloorFloatToInt32(wBounds1.x());
956     const int maxK1 = deFloorFloatToInt32(wBounds1.y());
957 
958     for (int k0 = minK0; k0 <= maxK0; k0++)
959     {
960         for (int j0 = minJ0; j0 <= maxJ0; j0++)
961         {
962             for (int i0 = minI0; i0 <= maxI0; i0++)
963             {
964                 for (int k1 = minK1; k1 <= maxK1; k1++)
965                 {
966                     for (int j1 = minJ1; j1 <= maxJ1; j1++)
967                     {
968                         for (int i1 = minI1; i1 <= maxI1; i1++)
969                         {
970                             const Vec4 c0 = lookup<float>(level0, sampler, wrap(sampler.wrapS, i0, w0),
971                                                           wrap(sampler.wrapT, j0, h0), wrap(sampler.wrapR, k0, d0));
972                             const Vec4 c1 = lookup<float>(level1, sampler, wrap(sampler.wrapS, i1, w1),
973                                                           wrap(sampler.wrapT, j1, h1), wrap(sampler.wrapR, k1, d1));
974 
975                             if (isLinearRangeValid(prec, c0, c1, fBounds, result))
976                                 return true;
977                         }
978                     }
979                 }
980             }
981         }
982     }
983 
984     return false;
985 }
986 
isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const float coordX,const int coordY,const Vec2 & fBounds,const Vec4 & result)987 static bool isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess &level0,
988                                                   const ConstPixelBufferAccess &level1, const Sampler &sampler,
989                                                   const LookupPrecision &prec, const float coordX, const int coordY,
990                                                   const Vec2 &fBounds, const Vec4 &result)
991 {
992     // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
993     //                           Right now this allows pairing any two valid bilinear quads.
994 
995     const int w0 = level0.getWidth();
996     const int w1 = level1.getWidth();
997 
998     const Vec2 uBounds0 =
999         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coordX, prec.coordBits.x(), prec.uvwBits.x());
1000     const Vec2 uBounds1 =
1001         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coordX, prec.coordBits.x(), prec.uvwBits.x());
1002 
1003     // Integer coordinates - without wrap mode
1004     const int minI0 = deFloorFloatToInt32(uBounds0.x() - 0.5f);
1005     const int maxI0 = deFloorFloatToInt32(uBounds0.y() - 0.5f);
1006     const int minI1 = deFloorFloatToInt32(uBounds1.x() - 0.5f);
1007     const int maxI1 = deFloorFloatToInt32(uBounds1.y() - 0.5f);
1008 
1009     const TextureChannelClass texClass = getTextureChannelClass(level0.getFormat().type);
1010     const float cSearchStep            = texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ?
1011                                              computeBilinearSearchStepForUnorm(prec) :
1012                                          texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ?
1013                                              computeBilinearSearchStepForSnorm(prec) :
1014                                              0.0f; // Step is computed for floating-point quads based on texel values.
1015 
1016     DE_ASSERT(texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
1017               texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || texClass == TEXTURECHANNELCLASS_FLOATING_POINT ||
1018               sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
1019 
1020     for (int i0 = minI0; i0 <= maxI0; i0++)
1021     {
1022         ColorLine line0;
1023         float searchStep0;
1024 
1025         {
1026             const int x0 = wrap(sampler.wrapS, i0, w0);
1027             const int x1 = wrap(sampler.wrapS, i0 + 1, w0);
1028             lookupLine(line0, level0, sampler, x0, x1, coordY);
1029 
1030             if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
1031                 searchStep0 = computeBilinearSearchStepFromFloatLine(prec, line0);
1032             else
1033                 searchStep0 = cSearchStep;
1034         }
1035 
1036         const float minA0 = de::clamp((uBounds0.x() - 0.5f) - float(i0), 0.0f, 1.0f);
1037         const float maxA0 = de::clamp((uBounds0.y() - 0.5f) - float(i0), 0.0f, 1.0f);
1038 
1039         for (int i1 = minI1; i1 <= maxI1; i1++)
1040         {
1041             ColorLine line1;
1042             float searchStep1;
1043 
1044             {
1045                 const int x0 = wrap(sampler.wrapS, i1, w1);
1046                 const int x1 = wrap(sampler.wrapS, i1 + 1, w1);
1047                 lookupLine(line1, level1, sampler, x0, x1, coordY);
1048 
1049                 if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
1050                     searchStep1 = computeBilinearSearchStepFromFloatLine(prec, line1);
1051                 else
1052                     searchStep1 = cSearchStep;
1053             }
1054 
1055             const float minA1 = de::clamp((uBounds1.x() - 0.5f) - float(i1), 0.0f, 1.0f);
1056             const float maxA1 = de::clamp((uBounds1.y() - 0.5f) - float(i1), 0.0f, 1.0f);
1057 
1058             if (is1DTrilinearFilterResultValid(prec, line0, line1, Vec2(minA0, maxA0), Vec2(minA1, maxA1), fBounds,
1059                                                de::min(searchStep0, searchStep1), result))
1060                 return true;
1061         }
1062     }
1063 
1064     return false;
1065 }
1066 
isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const Vec4 & result)1067 static bool isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess &level0,
1068                                                   const ConstPixelBufferAccess &level1, const Sampler &sampler,
1069                                                   const LookupPrecision &prec, const Vec2 &coord, const int coordZ,
1070                                                   const Vec2 &fBounds, const Vec4 &result)
1071 {
1072     // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
1073     //                           Right now this allows pairing any two valid bilinear quads.
1074 
1075     const int w0 = level0.getWidth();
1076     const int w1 = level1.getWidth();
1077     const int h0 = level0.getHeight();
1078     const int h1 = level1.getHeight();
1079 
1080     const Vec2 uBounds0 =
1081         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
1082     const Vec2 uBounds1 =
1083         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
1084     const Vec2 vBounds0 =
1085         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
1086     const Vec2 vBounds1 =
1087         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
1088 
1089     // Integer coordinates - without wrap mode
1090     const int minI0 = deFloorFloatToInt32(uBounds0.x() - 0.5f);
1091     const int maxI0 = deFloorFloatToInt32(uBounds0.y() - 0.5f);
1092     const int minI1 = deFloorFloatToInt32(uBounds1.x() - 0.5f);
1093     const int maxI1 = deFloorFloatToInt32(uBounds1.y() - 0.5f);
1094     const int minJ0 = deFloorFloatToInt32(vBounds0.x() - 0.5f);
1095     const int maxJ0 = deFloorFloatToInt32(vBounds0.y() - 0.5f);
1096     const int minJ1 = deFloorFloatToInt32(vBounds1.x() - 0.5f);
1097     const int maxJ1 = deFloorFloatToInt32(vBounds1.y() - 0.5f);
1098 
1099     const TextureChannelClass texClass = getTextureChannelClass(level0.getFormat().type);
1100     const float cSearchStep            = texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ?
1101                                              computeBilinearSearchStepForUnorm(prec) :
1102                                          texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ?
1103                                              computeBilinearSearchStepForSnorm(prec) :
1104                                              0.0f; // Step is computed for floating-point quads based on texel values.
1105 
1106     DE_ASSERT(texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
1107               texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || texClass == TEXTURECHANNELCLASS_FLOATING_POINT ||
1108               sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
1109 
1110     for (int j0 = minJ0; j0 <= maxJ0; j0++)
1111     {
1112         for (int i0 = minI0; i0 <= maxI0; i0++)
1113         {
1114             ColorQuad quad0;
1115             float searchStep0;
1116 
1117             {
1118                 const int x0 = wrap(sampler.wrapS, i0, w0);
1119                 const int x1 = wrap(sampler.wrapS, i0 + 1, w0);
1120                 const int y0 = wrap(sampler.wrapT, j0, h0);
1121                 const int y1 = wrap(sampler.wrapT, j0 + 1, h0);
1122                 lookupQuad(quad0, level0, sampler, x0, x1, y0, y1, coordZ);
1123 
1124                 if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
1125                     searchStep0 = computeBilinearSearchStepFromFloatQuad(prec, quad0);
1126                 else
1127                     searchStep0 = cSearchStep;
1128             }
1129 
1130             const float minA0 = de::clamp((uBounds0.x() - 0.5f) - float(i0), 0.0f, 1.0f);
1131             const float maxA0 = de::clamp((uBounds0.y() - 0.5f) - float(i0), 0.0f, 1.0f);
1132             const float minB0 = de::clamp((vBounds0.x() - 0.5f) - float(j0), 0.0f, 1.0f);
1133             const float maxB0 = de::clamp((vBounds0.y() - 0.5f) - float(j0), 0.0f, 1.0f);
1134 
1135             for (int j1 = minJ1; j1 <= maxJ1; j1++)
1136             {
1137                 for (int i1 = minI1; i1 <= maxI1; i1++)
1138                 {
1139                     ColorQuad quad1;
1140                     float searchStep1;
1141 
1142                     {
1143                         const int x0 = wrap(sampler.wrapS, i1, w1);
1144                         const int x1 = wrap(sampler.wrapS, i1 + 1, w1);
1145                         const int y0 = wrap(sampler.wrapT, j1, h1);
1146                         const int y1 = wrap(sampler.wrapT, j1 + 1, h1);
1147                         lookupQuad(quad1, level1, sampler, x0, x1, y0, y1, coordZ);
1148 
1149                         if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
1150                             searchStep1 = computeBilinearSearchStepFromFloatQuad(prec, quad1);
1151                         else
1152                             searchStep1 = cSearchStep;
1153                     }
1154 
1155                     const float minA1 = de::clamp((uBounds1.x() - 0.5f) - float(i1), 0.0f, 1.0f);
1156                     const float maxA1 = de::clamp((uBounds1.y() - 0.5f) - float(i1), 0.0f, 1.0f);
1157                     const float minB1 = de::clamp((vBounds1.x() - 0.5f) - float(j1), 0.0f, 1.0f);
1158                     const float maxB1 = de::clamp((vBounds1.y() - 0.5f) - float(j1), 0.0f, 1.0f);
1159 
1160                     if (is2DTrilinearFilterResultValid(prec, quad0, quad1, Vec2(minA0, maxA0), Vec2(minB0, maxB0),
1161                                                        Vec2(minA1, maxA1), Vec2(minB1, maxB1), fBounds,
1162                                                        de::min(searchStep0, searchStep1), result))
1163                         return true;
1164                 }
1165             }
1166         }
1167     }
1168 
1169     return false;
1170 }
1171 
isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & fBounds,const Vec4 & result)1172 static bool isLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess &level0,
1173                                                   const ConstPixelBufferAccess &level1, const Sampler &sampler,
1174                                                   const LookupPrecision &prec, const Vec3 &coord, const Vec2 &fBounds,
1175                                                   const Vec4 &result)
1176 {
1177     // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
1178     //                           Right now this allows pairing any two valid bilinear quads.
1179 
1180     const int w0 = level0.getWidth();
1181     const int w1 = level1.getWidth();
1182     const int h0 = level0.getHeight();
1183     const int h1 = level1.getHeight();
1184     const int d0 = level0.getDepth();
1185     const int d1 = level1.getDepth();
1186 
1187     const Vec2 uBounds0 =
1188         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
1189     const Vec2 uBounds1 =
1190         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
1191     const Vec2 vBounds0 =
1192         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
1193     const Vec2 vBounds1 =
1194         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
1195     const Vec2 wBounds0 =
1196         computeNonNormalizedCoordBounds(sampler.normalizedCoords, d0, coord.z(), prec.coordBits.z(), prec.uvwBits.z());
1197     const Vec2 wBounds1 =
1198         computeNonNormalizedCoordBounds(sampler.normalizedCoords, d1, coord.z(), prec.coordBits.z(), prec.uvwBits.z());
1199 
1200     // Integer coordinates - without wrap mode
1201     const int minI0 = deFloorFloatToInt32(uBounds0.x() - 0.5f);
1202     const int maxI0 = deFloorFloatToInt32(uBounds0.y() - 0.5f);
1203     const int minI1 = deFloorFloatToInt32(uBounds1.x() - 0.5f);
1204     const int maxI1 = deFloorFloatToInt32(uBounds1.y() - 0.5f);
1205     const int minJ0 = deFloorFloatToInt32(vBounds0.x() - 0.5f);
1206     const int maxJ0 = deFloorFloatToInt32(vBounds0.y() - 0.5f);
1207     const int minJ1 = deFloorFloatToInt32(vBounds1.x() - 0.5f);
1208     const int maxJ1 = deFloorFloatToInt32(vBounds1.y() - 0.5f);
1209     const int minK0 = deFloorFloatToInt32(wBounds0.x() - 0.5f);
1210     const int maxK0 = deFloorFloatToInt32(wBounds0.y() - 0.5f);
1211     const int minK1 = deFloorFloatToInt32(wBounds1.x() - 0.5f);
1212     const int maxK1 = deFloorFloatToInt32(wBounds1.y() - 0.5f);
1213 
1214     const TextureChannelClass texClass = getTextureChannelClass(level0.getFormat().type);
1215     const float cSearchStep            = texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ?
1216                                              computeBilinearSearchStepForUnorm(prec) :
1217                                          texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ?
1218                                              computeBilinearSearchStepForSnorm(prec) :
1219                                              0.0f; // Step is computed for floating-point quads based on texel values.
1220 
1221     DE_ASSERT(texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
1222               texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || texClass == TEXTURECHANNELCLASS_FLOATING_POINT ||
1223               sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
1224 
1225     for (int k0 = minK0; k0 <= maxK0; k0++)
1226     {
1227         for (int j0 = minJ0; j0 <= maxJ0; j0++)
1228         {
1229             for (int i0 = minI0; i0 <= maxI0; i0++)
1230             {
1231                 ColorQuad quad00, quad01;
1232                 float searchStep0;
1233 
1234                 {
1235                     const int x0 = wrap(sampler.wrapS, i0, w0);
1236                     const int x1 = wrap(sampler.wrapS, i0 + 1, w0);
1237                     const int y0 = wrap(sampler.wrapT, j0, h0);
1238                     const int y1 = wrap(sampler.wrapT, j0 + 1, h0);
1239                     const int z0 = wrap(sampler.wrapR, k0, d0);
1240                     const int z1 = wrap(sampler.wrapR, k0 + 1, d0);
1241                     lookupQuad(quad00, level0, sampler, x0, x1, y0, y1, z0);
1242                     lookupQuad(quad01, level0, sampler, x0, x1, y0, y1, z1);
1243 
1244                     if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
1245                         searchStep0 = de::min(computeBilinearSearchStepFromFloatQuad(prec, quad00),
1246                                               computeBilinearSearchStepFromFloatQuad(prec, quad01));
1247                     else
1248                         searchStep0 = cSearchStep;
1249                 }
1250 
1251                 const float minA0 = de::clamp((uBounds0.x() - 0.5f) - float(i0), 0.0f, 1.0f);
1252                 const float maxA0 = de::clamp((uBounds0.y() - 0.5f) - float(i0), 0.0f, 1.0f);
1253                 const float minB0 = de::clamp((vBounds0.x() - 0.5f) - float(j0), 0.0f, 1.0f);
1254                 const float maxB0 = de::clamp((vBounds0.y() - 0.5f) - float(j0), 0.0f, 1.0f);
1255                 const float minC0 = de::clamp((wBounds0.x() - 0.5f) - float(k0), 0.0f, 1.0f);
1256                 const float maxC0 = de::clamp((wBounds0.y() - 0.5f) - float(k0), 0.0f, 1.0f);
1257 
1258                 for (int k1 = minK1; k1 <= maxK1; k1++)
1259                 {
1260                     for (int j1 = minJ1; j1 <= maxJ1; j1++)
1261                     {
1262                         for (int i1 = minI1; i1 <= maxI1; i1++)
1263                         {
1264                             ColorQuad quad10, quad11;
1265                             float searchStep1;
1266 
1267                             {
1268                                 const int x0 = wrap(sampler.wrapS, i1, w1);
1269                                 const int x1 = wrap(sampler.wrapS, i1 + 1, w1);
1270                                 const int y0 = wrap(sampler.wrapT, j1, h1);
1271                                 const int y1 = wrap(sampler.wrapT, j1 + 1, h1);
1272                                 const int z0 = wrap(sampler.wrapR, k1, d1);
1273                                 const int z1 = wrap(sampler.wrapR, k1 + 1, d1);
1274                                 lookupQuad(quad10, level1, sampler, x0, x1, y0, y1, z0);
1275                                 lookupQuad(quad11, level1, sampler, x0, x1, y0, y1, z1);
1276 
1277                                 if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
1278                                     searchStep1 = de::min(computeBilinearSearchStepFromFloatQuad(prec, quad10),
1279                                                           computeBilinearSearchStepFromFloatQuad(prec, quad11));
1280                                 else
1281                                     searchStep1 = cSearchStep;
1282                             }
1283 
1284                             const float minA1 = de::clamp((uBounds1.x() - 0.5f) - float(i1), 0.0f, 1.0f);
1285                             const float maxA1 = de::clamp((uBounds1.y() - 0.5f) - float(i1), 0.0f, 1.0f);
1286                             const float minB1 = de::clamp((vBounds1.x() - 0.5f) - float(j1), 0.0f, 1.0f);
1287                             const float maxB1 = de::clamp((vBounds1.y() - 0.5f) - float(j1), 0.0f, 1.0f);
1288                             const float minC1 = de::clamp((wBounds1.x() - 0.5f) - float(k1), 0.0f, 1.0f);
1289                             const float maxC1 = de::clamp((wBounds1.y() - 0.5f) - float(k1), 0.0f, 1.0f);
1290 
1291                             if (is3DTrilinearFilterResultValid(
1292                                     prec, quad00, quad01, quad10, quad11, Vec2(minA0, maxA0), Vec2(minB0, maxB0),
1293                                     Vec2(minC0, maxC0), Vec2(minA1, maxA1), Vec2(minB1, maxB1), Vec2(minC1, maxC1),
1294                                     fBounds, de::min(searchStep0, searchStep1), result))
1295                                 return true;
1296                         }
1297                     }
1298                 }
1299             }
1300         }
1301     }
1302 
1303     return false;
1304 }
1305 
isLevelSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const float coordX,const int coordY,const Vec4 & result)1306 static bool isLevelSampleResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
1307                                      const Sampler::FilterMode filterMode, const LookupPrecision &prec,
1308                                      const float coordX, const int coordY, const Vec4 &result)
1309 {
1310     if (filterMode == Sampler::LINEAR)
1311         return isLinearSampleResultValid(level, sampler, prec, coordX, coordY, result);
1312     else
1313         return isNearestSampleResultValid(level, sampler, prec, coordX, coordY, result);
1314 }
1315 
isLevelSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec4 & result)1316 static bool isLevelSampleResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
1317                                      const Sampler::FilterMode filterMode, const LookupPrecision &prec,
1318                                      const Vec2 &coord, const int coordZ, const Vec4 &result)
1319 {
1320     if (filterMode == Sampler::LINEAR)
1321         return isLinearSampleResultValid(level, sampler, prec, coord, coordZ, result);
1322     else
1323         return isNearestSampleResultValid(level, sampler, prec, coord, coordZ, result);
1324 }
1325 
isMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const float coordX,const int coordY,const Vec2 & fBounds,const Vec4 & result)1326 static bool isMipmapLinearSampleResultValid(const ConstPixelBufferAccess &level0, const ConstPixelBufferAccess &level1,
1327                                             const Sampler &sampler, const Sampler::FilterMode levelFilter,
1328                                             const LookupPrecision &prec, const float coordX, const int coordY,
1329                                             const Vec2 &fBounds, const Vec4 &result)
1330 {
1331     if (levelFilter == Sampler::LINEAR)
1332         return isLinearMipmapLinearSampleResultValid(level0, level1, sampler, prec, coordX, coordY, fBounds, result);
1333     else
1334         return isNearestMipmapLinearSampleResultValid(level0, level1, sampler, prec, coordX, coordY, fBounds, result);
1335 }
1336 
isMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const Vec4 & result)1337 static bool isMipmapLinearSampleResultValid(const ConstPixelBufferAccess &level0, const ConstPixelBufferAccess &level1,
1338                                             const Sampler &sampler, const Sampler::FilterMode levelFilter,
1339                                             const LookupPrecision &prec, const Vec2 &coord, const int coordZ,
1340                                             const Vec2 &fBounds, const Vec4 &result)
1341 {
1342     if (levelFilter == Sampler::LINEAR)
1343         return isLinearMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, result);
1344     else
1345         return isNearestMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, result);
1346 }
1347 
isLookupResultValid(const Texture2DView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const Vec2 & lodBounds,const Vec4 & result)1348 bool isLookupResultValid(const Texture2DView &texture, const Sampler &sampler, const LookupPrecision &prec,
1349                          const Vec2 &coord, const Vec2 &lodBounds, const Vec4 &result)
1350 {
1351     const float minLod        = lodBounds.x();
1352     const float maxLod        = lodBounds.y();
1353     const bool canBeMagnified = minLod <= sampler.lodThreshold;
1354     const bool canBeMinified  = maxLod > sampler.lodThreshold;
1355 
1356     DE_ASSERT(isSamplerSupported(sampler));
1357 
1358     if (canBeMagnified)
1359     {
1360         if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, result))
1361             return true;
1362     }
1363 
1364     if (canBeMinified)
1365     {
1366         const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
1367         const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
1368         const int minTexLevel      = 0;
1369         const int maxTexLevel      = texture.getNumLevels() - 1;
1370 
1371         DE_ASSERT(minTexLevel <= maxTexLevel);
1372 
1373         if (isLinearMipmap && minTexLevel < maxTexLevel)
1374         {
1375             const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
1376             const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
1377 
1378             DE_ASSERT(minLevel <= maxLevel);
1379 
1380             for (int level = minLevel; level <= maxLevel; level++)
1381             {
1382                 const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
1383                 const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
1384 
1385                 if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler,
1386                                                     getLevelFilter(sampler.minFilter), prec, coord, 0, Vec2(minF, maxF),
1387                                                     result))
1388                     return true;
1389             }
1390         }
1391         else if (isNearestMipmap)
1392         {
1393             // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1394             //         decision to allow floor(lod + 0.5) as well.
1395             const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
1396             const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
1397 
1398             DE_ASSERT(minLevel <= maxLevel);
1399 
1400             for (int level = minLevel; level <= maxLevel; level++)
1401             {
1402                 if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec,
1403                                              coord, 0, result))
1404                     return true;
1405             }
1406         }
1407         else
1408         {
1409             if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, result))
1410                 return true;
1411         }
1412     }
1413 
1414     return false;
1415 }
1416 
isLookupResultValid(const Texture1DView & texture,const Sampler & sampler,const LookupPrecision & prec,const float coord,const Vec2 & lodBounds,const Vec4 & result)1417 bool isLookupResultValid(const Texture1DView &texture, const Sampler &sampler, const LookupPrecision &prec,
1418                          const float coord, const Vec2 &lodBounds, const Vec4 &result)
1419 {
1420     const float minLod        = lodBounds.x();
1421     const float maxLod        = lodBounds.y();
1422     const bool canBeMagnified = minLod <= sampler.lodThreshold;
1423     const bool canBeMinified  = maxLod > sampler.lodThreshold;
1424 
1425     DE_ASSERT(isSamplerSupported(sampler));
1426 
1427     if (canBeMagnified)
1428     {
1429         if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, result))
1430             return true;
1431     }
1432 
1433     if (canBeMinified)
1434     {
1435         const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
1436         const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
1437         const int minTexLevel      = 0;
1438         const int maxTexLevel      = texture.getNumLevels() - 1;
1439 
1440         DE_ASSERT(minTexLevel <= maxTexLevel);
1441 
1442         if (isLinearMipmap && minTexLevel < maxTexLevel)
1443         {
1444             const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
1445             const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
1446 
1447             DE_ASSERT(minLevel <= maxLevel);
1448 
1449             for (int level = minLevel; level <= maxLevel; level++)
1450             {
1451                 const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
1452                 const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
1453 
1454                 if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler,
1455                                                     getLevelFilter(sampler.minFilter), prec, coord, 0, Vec2(minF, maxF),
1456                                                     result))
1457                     return true;
1458             }
1459         }
1460         else if (isNearestMipmap)
1461         {
1462             // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1463             //         decision to allow floor(lod + 0.5) as well.
1464             const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
1465             const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
1466 
1467             DE_ASSERT(minLevel <= maxLevel);
1468 
1469             for (int level = minLevel; level <= maxLevel; level++)
1470             {
1471                 if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec,
1472                                              coord, 0, result))
1473                     return true;
1474             }
1475         }
1476         else
1477         {
1478             if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, result))
1479                 return true;
1480         }
1481     }
1482 
1483     return false;
1484 }
1485 
isSeamlessLinearSampleResultValid(const ConstPixelBufferAccess (& faces)[CUBEFACE_LAST],const Sampler & sampler,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec4 & result)1486 static bool isSeamlessLinearSampleResultValid(const ConstPixelBufferAccess (&faces)[CUBEFACE_LAST],
1487                                               const Sampler &sampler, const LookupPrecision &prec,
1488                                               const CubeFaceFloatCoords &coords, const Vec4 &result)
1489 {
1490     const int size = faces[coords.face].getWidth();
1491 
1492     const Vec2 uBounds =
1493         computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
1494     const Vec2 vBounds =
1495         computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
1496 
1497     // Integer coordinate bounds for (x0,y0) - without wrap mode
1498     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
1499     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
1500     const int minJ = deFloorFloatToInt32(vBounds.x() - 0.5f);
1501     const int maxJ = deFloorFloatToInt32(vBounds.y() - 0.5f);
1502 
1503     const TextureChannelClass texClass = getTextureChannelClass(faces[coords.face].getFormat().type);
1504     float searchStep                   = texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ?
1505                                              computeBilinearSearchStepForUnorm(prec) :
1506                                          texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ?
1507                                              computeBilinearSearchStepForSnorm(prec) :
1508                                              0.0f; // Step is computed for floating-point quads based on texel values.
1509 
1510     DE_ASSERT(texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
1511               texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || texClass == TEXTURECHANNELCLASS_FLOATING_POINT ||
1512               sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
1513 
1514     for (int j = minJ; j <= maxJ; j++)
1515     {
1516         for (int i = minI; i <= maxI; i++)
1517         {
1518             const CubeFaceIntCoords c00 =
1519                 remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i + 0, j + 0)), size);
1520             const CubeFaceIntCoords c10 =
1521                 remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i + 1, j + 0)), size);
1522             const CubeFaceIntCoords c01 =
1523                 remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i + 0, j + 1)), size);
1524             const CubeFaceIntCoords c11 =
1525                 remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i + 1, j + 1)), size);
1526 
1527             // If any of samples is out of both edges, implementations can do pretty much anything according to spec.
1528             // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
1529             if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST ||
1530                 c11.face == CUBEFACE_LAST)
1531                 return true;
1532 
1533             // Bounds for filtering factors
1534             const float minA = de::clamp((uBounds.x() - 0.5f) - float(i), 0.0f, 1.0f);
1535             const float maxA = de::clamp((uBounds.y() - 0.5f) - float(i), 0.0f, 1.0f);
1536             const float minB = de::clamp((vBounds.x() - 0.5f) - float(j), 0.0f, 1.0f);
1537             const float maxB = de::clamp((vBounds.y() - 0.5f) - float(j), 0.0f, 1.0f);
1538 
1539             ColorQuad quad;
1540             quad.p00 = lookup<float>(faces[c00.face], sampler, c00.s, c00.t, 0);
1541             quad.p10 = lookup<float>(faces[c10.face], sampler, c10.s, c10.t, 0);
1542             quad.p01 = lookup<float>(faces[c01.face], sampler, c01.s, c01.t, 0);
1543             quad.p11 = lookup<float>(faces[c11.face], sampler, c11.s, c11.t, 0);
1544 
1545             if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
1546                 searchStep = computeBilinearSearchStepFromFloatQuad(prec, quad);
1547 
1548             if (sampler.reductionMode == Sampler::WEIGHTED_AVERAGE)
1549             {
1550                 if (isBilinearRangeValid(prec, quad, Vec2(minA, maxA), Vec2(minB, maxB), searchStep, result))
1551                     return true;
1552             }
1553             else
1554             {
1555                 if (isReductionValid(prec, quad, sampler.reductionMode, result))
1556                     return true;
1557             }
1558         }
1559     }
1560 
1561     return false;
1562 }
1563 
isSeamplessLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess (& faces0)[CUBEFACE_LAST],const ConstPixelBufferAccess (& faces1)[CUBEFACE_LAST],const Sampler & sampler,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec2 & fBounds,const Vec4 & result)1564 static bool isSeamplessLinearMipmapLinearSampleResultValid(const ConstPixelBufferAccess (&faces0)[CUBEFACE_LAST],
1565                                                            const ConstPixelBufferAccess (&faces1)[CUBEFACE_LAST],
1566                                                            const Sampler &sampler, const LookupPrecision &prec,
1567                                                            const CubeFaceFloatCoords &coords, const Vec2 &fBounds,
1568                                                            const Vec4 &result)
1569 {
1570     // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
1571     //                           Right now this allows pairing any two valid bilinear quads.
1572 
1573     const int size0 = faces0[coords.face].getWidth();
1574     const int size1 = faces1[coords.face].getWidth();
1575 
1576     const Vec2 uBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.s, prec.coordBits.x(),
1577                                                           prec.uvwBits.x());
1578     const Vec2 uBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.s, prec.coordBits.x(),
1579                                                           prec.uvwBits.x());
1580     const Vec2 vBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.t, prec.coordBits.y(),
1581                                                           prec.uvwBits.y());
1582     const Vec2 vBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.t, prec.coordBits.y(),
1583                                                           prec.uvwBits.y());
1584 
1585     // Integer coordinates - without wrap mode
1586     const int minI0 = deFloorFloatToInt32(uBounds0.x() - 0.5f);
1587     const int maxI0 = deFloorFloatToInt32(uBounds0.y() - 0.5f);
1588     const int minI1 = deFloorFloatToInt32(uBounds1.x() - 0.5f);
1589     const int maxI1 = deFloorFloatToInt32(uBounds1.y() - 0.5f);
1590     const int minJ0 = deFloorFloatToInt32(vBounds0.x() - 0.5f);
1591     const int maxJ0 = deFloorFloatToInt32(vBounds0.y() - 0.5f);
1592     const int minJ1 = deFloorFloatToInt32(vBounds1.x() - 0.5f);
1593     const int maxJ1 = deFloorFloatToInt32(vBounds1.y() - 0.5f);
1594 
1595     const TextureChannelClass texClass = getTextureChannelClass(faces0[coords.face].getFormat().type);
1596     const float cSearchStep            = texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ?
1597                                              computeBilinearSearchStepForUnorm(prec) :
1598                                          texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT ?
1599                                              computeBilinearSearchStepForSnorm(prec) :
1600                                              0.0f; // Step is computed for floating-point quads based on texel values.
1601 
1602     DE_ASSERT(texClass == TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
1603               texClass == TEXTURECHANNELCLASS_SIGNED_FIXED_POINT || texClass == TEXTURECHANNELCLASS_FLOATING_POINT ||
1604               sampler.reductionMode != Sampler::WEIGHTED_AVERAGE);
1605 
1606     for (int j0 = minJ0; j0 <= maxJ0; j0++)
1607     {
1608         for (int i0 = minI0; i0 <= maxI0; i0++)
1609         {
1610             ColorQuad quad0;
1611             float searchStep0;
1612 
1613             {
1614                 const CubeFaceIntCoords c00 =
1615                     remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0 + 0, j0 + 0)), size0);
1616                 const CubeFaceIntCoords c10 =
1617                     remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0 + 1, j0 + 0)), size0);
1618                 const CubeFaceIntCoords c01 =
1619                     remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0 + 0, j0 + 1)), size0);
1620                 const CubeFaceIntCoords c11 =
1621                     remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0 + 1, j0 + 1)), size0);
1622 
1623                 // If any of samples is out of both edges, implementations can do pretty much anything according to spec.
1624                 // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
1625                 if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST ||
1626                     c11.face == CUBEFACE_LAST)
1627                     return true;
1628 
1629                 quad0.p00 = lookup<float>(faces0[c00.face], sampler, c00.s, c00.t, 0);
1630                 quad0.p10 = lookup<float>(faces0[c10.face], sampler, c10.s, c10.t, 0);
1631                 quad0.p01 = lookup<float>(faces0[c01.face], sampler, c01.s, c01.t, 0);
1632                 quad0.p11 = lookup<float>(faces0[c11.face], sampler, c11.s, c11.t, 0);
1633 
1634                 if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
1635                     searchStep0 = computeBilinearSearchStepFromFloatQuad(prec, quad0);
1636                 else
1637                     searchStep0 = cSearchStep;
1638             }
1639 
1640             const float minA0 = de::clamp((uBounds0.x() - 0.5f) - float(i0), 0.0f, 1.0f);
1641             const float maxA0 = de::clamp((uBounds0.y() - 0.5f) - float(i0), 0.0f, 1.0f);
1642             const float minB0 = de::clamp((vBounds0.x() - 0.5f) - float(j0), 0.0f, 1.0f);
1643             const float maxB0 = de::clamp((vBounds0.y() - 0.5f) - float(j0), 0.0f, 1.0f);
1644 
1645             for (int j1 = minJ1; j1 <= maxJ1; j1++)
1646             {
1647                 for (int i1 = minI1; i1 <= maxI1; i1++)
1648                 {
1649                     ColorQuad quad1;
1650                     float searchStep1;
1651 
1652                     {
1653                         const CubeFaceIntCoords c00 =
1654                             remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1 + 0, j1 + 0)), size1);
1655                         const CubeFaceIntCoords c10 =
1656                             remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1 + 1, j1 + 0)), size1);
1657                         const CubeFaceIntCoords c01 =
1658                             remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1 + 0, j1 + 1)), size1);
1659                         const CubeFaceIntCoords c11 =
1660                             remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1 + 1, j1 + 1)), size1);
1661 
1662                         if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST ||
1663                             c11.face == CUBEFACE_LAST)
1664                             return true;
1665 
1666                         quad1.p00 = lookup<float>(faces1[c00.face], sampler, c00.s, c00.t, 0);
1667                         quad1.p10 = lookup<float>(faces1[c10.face], sampler, c10.s, c10.t, 0);
1668                         quad1.p01 = lookup<float>(faces1[c01.face], sampler, c01.s, c01.t, 0);
1669                         quad1.p11 = lookup<float>(faces1[c11.face], sampler, c11.s, c11.t, 0);
1670 
1671                         if (texClass == TEXTURECHANNELCLASS_FLOATING_POINT)
1672                             searchStep1 = computeBilinearSearchStepFromFloatQuad(prec, quad1);
1673                         else
1674                             searchStep1 = cSearchStep;
1675                     }
1676 
1677                     const float minA1 = de::clamp((uBounds1.x() - 0.5f) - float(i1), 0.0f, 1.0f);
1678                     const float maxA1 = de::clamp((uBounds1.y() - 0.5f) - float(i1), 0.0f, 1.0f);
1679                     const float minB1 = de::clamp((vBounds1.x() - 0.5f) - float(j1), 0.0f, 1.0f);
1680                     const float maxB1 = de::clamp((vBounds1.y() - 0.5f) - float(j1), 0.0f, 1.0f);
1681 
1682                     if (is2DTrilinearFilterResultValid(prec, quad0, quad1, Vec2(minA0, maxA0), Vec2(minB0, maxB0),
1683                                                        Vec2(minA1, maxA1), Vec2(minB1, maxB1), fBounds,
1684                                                        de::min(searchStep0, searchStep1), result))
1685                         return true;
1686                 }
1687             }
1688         }
1689     }
1690 
1691     return false;
1692 }
1693 
isCubeLevelSampleResultValid(const ConstPixelBufferAccess (& level)[CUBEFACE_LAST],const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec4 & result)1694 static bool isCubeLevelSampleResultValid(const ConstPixelBufferAccess (&level)[CUBEFACE_LAST], const Sampler &sampler,
1695                                          const Sampler::FilterMode filterMode, const LookupPrecision &prec,
1696                                          const CubeFaceFloatCoords &coords, const Vec4 &result)
1697 {
1698     if (filterMode == Sampler::LINEAR)
1699     {
1700         if (sampler.seamlessCubeMap)
1701             return isSeamlessLinearSampleResultValid(level, sampler, prec, coords, result);
1702         else
1703             return isLinearSampleResultValid(level[coords.face], sampler, prec, Vec2(coords.s, coords.t), 0, result);
1704     }
1705     else
1706         return isNearestSampleResultValid(level[coords.face], sampler, prec, Vec2(coords.s, coords.t), 0, result);
1707 }
1708 
isCubeMipmapLinearSampleResultValid(const ConstPixelBufferAccess (& faces0)[CUBEFACE_LAST],const ConstPixelBufferAccess (& faces1)[CUBEFACE_LAST],const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const CubeFaceFloatCoords & coords,const Vec2 & fBounds,const Vec4 & result)1709 static bool isCubeMipmapLinearSampleResultValid(const ConstPixelBufferAccess (&faces0)[CUBEFACE_LAST],
1710                                                 const ConstPixelBufferAccess (&faces1)[CUBEFACE_LAST],
1711                                                 const Sampler &sampler, const Sampler::FilterMode levelFilter,
1712                                                 const LookupPrecision &prec, const CubeFaceFloatCoords &coords,
1713                                                 const Vec2 &fBounds, const Vec4 &result)
1714 {
1715     if (levelFilter == Sampler::LINEAR)
1716     {
1717         if (sampler.seamlessCubeMap)
1718             return isSeamplessLinearMipmapLinearSampleResultValid(faces0, faces1, sampler, prec, coords, fBounds,
1719                                                                   result);
1720         else
1721             return isLinearMipmapLinearSampleResultValid(faces0[coords.face], faces1[coords.face], sampler, prec,
1722                                                          Vec2(coords.s, coords.t), 0, fBounds, result);
1723     }
1724     else
1725         return isNearestMipmapLinearSampleResultValid(faces0[coords.face], faces1[coords.face], sampler, prec,
1726                                                       Vec2(coords.s, coords.t), 0, fBounds, result);
1727 }
1728 
getCubeLevelFaces(const TextureCubeView & texture,const int levelNdx,ConstPixelBufferAccess (& out)[CUBEFACE_LAST])1729 static void getCubeLevelFaces(const TextureCubeView &texture, const int levelNdx,
1730                               ConstPixelBufferAccess (&out)[CUBEFACE_LAST])
1731 {
1732     for (int faceNdx = 0; faceNdx < CUBEFACE_LAST; faceNdx++)
1733         out[faceNdx] = texture.getLevelFace(levelNdx, (CubeFace)faceNdx);
1734 }
1735 
isLookupResultValid(const TextureCubeView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const Vec4 & result)1736 bool isLookupResultValid(const TextureCubeView &texture, const Sampler &sampler, const LookupPrecision &prec,
1737                          const Vec3 &coord, const Vec2 &lodBounds, const Vec4 &result)
1738 {
1739     int numPossibleFaces = 0;
1740     CubeFace possibleFaces[CUBEFACE_LAST];
1741 
1742     DE_ASSERT(isSamplerSupported(sampler));
1743 
1744     getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
1745 
1746     if (numPossibleFaces == 0)
1747         return true; // Result is undefined.
1748 
1749     for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
1750     {
1751         const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx],
1752                                              projectToFace(possibleFaces[tryFaceNdx], coord));
1753         const float minLod        = lodBounds.x();
1754         const float maxLod        = lodBounds.y();
1755         const bool canBeMagnified = minLod <= sampler.lodThreshold;
1756         const bool canBeMinified  = maxLod > sampler.lodThreshold;
1757 
1758         if (canBeMagnified)
1759         {
1760             ConstPixelBufferAccess faces[CUBEFACE_LAST];
1761             getCubeLevelFaces(texture, 0, faces);
1762 
1763             if (isCubeLevelSampleResultValid(faces, sampler, sampler.magFilter, prec, faceCoords, result))
1764                 return true;
1765         }
1766 
1767         if (canBeMinified)
1768         {
1769             const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
1770             const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
1771             const int minTexLevel      = 0;
1772             const int maxTexLevel      = texture.getNumLevels() - 1;
1773 
1774             DE_ASSERT(minTexLevel <= maxTexLevel);
1775 
1776             if (isLinearMipmap && minTexLevel < maxTexLevel)
1777             {
1778                 const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
1779                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
1780 
1781                 DE_ASSERT(minLevel <= maxLevel);
1782 
1783                 for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
1784                 {
1785                     const float minF = de::clamp(minLod - float(levelNdx), 0.0f, 1.0f);
1786                     const float maxF = de::clamp(maxLod - float(levelNdx), 0.0f, 1.0f);
1787 
1788                     ConstPixelBufferAccess faces0[CUBEFACE_LAST];
1789                     ConstPixelBufferAccess faces1[CUBEFACE_LAST];
1790 
1791                     getCubeLevelFaces(texture, levelNdx, faces0);
1792                     getCubeLevelFaces(texture, levelNdx + 1, faces1);
1793 
1794                     if (isCubeMipmapLinearSampleResultValid(faces0, faces1, sampler, getLevelFilter(sampler.minFilter),
1795                                                             prec, faceCoords, Vec2(minF, maxF), result))
1796                         return true;
1797                 }
1798             }
1799             else if (isNearestMipmap)
1800             {
1801                 // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1802                 //         decision to allow floor(lod + 0.5) as well.
1803                 const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
1804                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
1805 
1806                 DE_ASSERT(minLevel <= maxLevel);
1807 
1808                 for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
1809                 {
1810                     ConstPixelBufferAccess faces[CUBEFACE_LAST];
1811                     getCubeLevelFaces(texture, levelNdx, faces);
1812 
1813                     if (isCubeLevelSampleResultValid(faces, sampler, getLevelFilter(sampler.minFilter), prec,
1814                                                      faceCoords, result))
1815                         return true;
1816                 }
1817             }
1818             else
1819             {
1820                 ConstPixelBufferAccess faces[CUBEFACE_LAST];
1821                 getCubeLevelFaces(texture, 0, faces);
1822 
1823                 if (isCubeLevelSampleResultValid(faces, sampler, sampler.minFilter, prec, faceCoords, result))
1824                     return true;
1825             }
1826         }
1827     }
1828 
1829     return false;
1830 }
1831 
computeLayerRange(int numLayers,int numCoordBits,float layerCoord)1832 static inline IVec2 computeLayerRange(int numLayers, int numCoordBits, float layerCoord)
1833 {
1834     const float err = computeFloatingPointError(layerCoord, numCoordBits);
1835     const int minL  = (int)deFloatFloor(layerCoord - err + 0.5f);    // Round down
1836     const int maxL  = (int)deFloatCeil(layerCoord + err + 0.5f) - 1; // Round up
1837 
1838     DE_ASSERT(minL <= maxL);
1839 
1840     return IVec2(de::clamp(minL, 0, numLayers - 1), de::clamp(maxL, 0, numLayers - 1));
1841 }
1842 
isLookupResultValid(const Texture1DArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,const Vec2 & lodBounds,const Vec4 & result)1843 bool isLookupResultValid(const Texture1DArrayView &texture, const Sampler &sampler, const LookupPrecision &prec,
1844                          const Vec2 &coord, const Vec2 &lodBounds, const Vec4 &result)
1845 {
1846     const IVec2 layerRange    = computeLayerRange(texture.getNumLayers(), prec.coordBits.y(), coord.y());
1847     const float coordX        = coord.x();
1848     const float minLod        = lodBounds.x();
1849     const float maxLod        = lodBounds.y();
1850     const bool canBeMagnified = minLod <= sampler.lodThreshold;
1851     const bool canBeMinified  = maxLod > sampler.lodThreshold;
1852 
1853     DE_ASSERT(isSamplerSupported(sampler));
1854 
1855     for (int layer = layerRange.x(); layer <= layerRange.y(); layer++)
1856     {
1857         if (canBeMagnified)
1858         {
1859             if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coordX, layer, result))
1860                 return true;
1861         }
1862 
1863         if (canBeMinified)
1864         {
1865             const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
1866             const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
1867             const int minTexLevel      = 0;
1868             const int maxTexLevel      = texture.getNumLevels() - 1;
1869 
1870             DE_ASSERT(minTexLevel <= maxTexLevel);
1871 
1872             if (isLinearMipmap && minTexLevel < maxTexLevel)
1873             {
1874                 const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
1875                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
1876 
1877                 DE_ASSERT(minLevel <= maxLevel);
1878 
1879                 for (int level = minLevel; level <= maxLevel; level++)
1880                 {
1881                     const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
1882                     const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
1883 
1884                     if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler,
1885                                                         getLevelFilter(sampler.minFilter), prec, coordX, layer,
1886                                                         Vec2(minF, maxF), result))
1887                         return true;
1888                 }
1889             }
1890             else if (isNearestMipmap)
1891             {
1892                 // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1893                 //         decision to allow floor(lod + 0.5) as well.
1894                 const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
1895                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
1896 
1897                 DE_ASSERT(minLevel <= maxLevel);
1898 
1899                 for (int level = minLevel; level <= maxLevel; level++)
1900                 {
1901                     if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter),
1902                                                  prec, coordX, layer, result))
1903                         return true;
1904                 }
1905             }
1906             else
1907             {
1908                 if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coordX, layer,
1909                                              result))
1910                     return true;
1911             }
1912         }
1913     }
1914 
1915     return false;
1916 }
1917 
isLookupResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const Vec4 & result)1918 bool isLookupResultValid(const Texture2DArrayView &texture, const Sampler &sampler, const LookupPrecision &prec,
1919                          const Vec3 &coord, const Vec2 &lodBounds, const Vec4 &result)
1920 {
1921     const IVec2 layerRange    = computeLayerRange(texture.getNumLayers(), prec.coordBits.z(), coord.z());
1922     const Vec2 coordXY        = coord.swizzle(0, 1);
1923     const float minLod        = lodBounds.x();
1924     const float maxLod        = lodBounds.y();
1925     const bool canBeMagnified = minLod <= sampler.lodThreshold;
1926     const bool canBeMinified  = maxLod > sampler.lodThreshold;
1927 
1928     DE_ASSERT(isSamplerSupported(sampler));
1929 
1930     for (int layer = layerRange.x(); layer <= layerRange.y(); layer++)
1931     {
1932         if (canBeMagnified)
1933         {
1934             if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coordXY, layer, result))
1935                 return true;
1936         }
1937 
1938         if (canBeMinified)
1939         {
1940             const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
1941             const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
1942             const int minTexLevel      = 0;
1943             const int maxTexLevel      = texture.getNumLevels() - 1;
1944 
1945             DE_ASSERT(minTexLevel <= maxTexLevel);
1946 
1947             if (isLinearMipmap && minTexLevel < maxTexLevel)
1948             {
1949                 const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
1950                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
1951 
1952                 DE_ASSERT(minLevel <= maxLevel);
1953 
1954                 for (int level = minLevel; level <= maxLevel; level++)
1955                 {
1956                     const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
1957                     const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
1958 
1959                     if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler,
1960                                                         getLevelFilter(sampler.minFilter), prec, coordXY, layer,
1961                                                         Vec2(minF, maxF), result))
1962                         return true;
1963                 }
1964             }
1965             else if (isNearestMipmap)
1966             {
1967                 // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1968                 //         decision to allow floor(lod + 0.5) as well.
1969                 const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
1970                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
1971 
1972                 DE_ASSERT(minLevel <= maxLevel);
1973 
1974                 for (int level = minLevel; level <= maxLevel; level++)
1975                 {
1976                     if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter),
1977                                                  prec, coordXY, layer, result))
1978                         return true;
1979                 }
1980             }
1981             else
1982             {
1983                 if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coordXY, layer,
1984                                              result))
1985                     return true;
1986             }
1987         }
1988     }
1989 
1990     return false;
1991 }
1992 
isLevelSampleResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const LookupPrecision & prec,const Vec3 & coord,const Vec4 & result)1993 static bool isLevelSampleResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
1994                                      const Sampler::FilterMode filterMode, const LookupPrecision &prec,
1995                                      const Vec3 &coord, const Vec4 &result)
1996 {
1997     if (filterMode == Sampler::LINEAR)
1998         return isLinearSampleResultValid(level, sampler, prec, coord, result);
1999     else
2000         return isNearestSampleResultValid(level, sampler, prec, coord, result);
2001 }
2002 
isMipmapLinearSampleResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & fBounds,const Vec4 & result)2003 static bool isMipmapLinearSampleResultValid(const ConstPixelBufferAccess &level0, const ConstPixelBufferAccess &level1,
2004                                             const Sampler &sampler, const Sampler::FilterMode levelFilter,
2005                                             const LookupPrecision &prec, const Vec3 &coord, const Vec2 &fBounds,
2006                                             const Vec4 &result)
2007 {
2008     if (levelFilter == Sampler::LINEAR)
2009         return isLinearMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, fBounds, result);
2010     else
2011         return isNearestMipmapLinearSampleResultValid(level0, level1, sampler, prec, coord, fBounds, result);
2012 }
2013 
isLookupResultValid(const Texture3DView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const Vec4 & result)2014 bool isLookupResultValid(const Texture3DView &texture, const Sampler &sampler, const LookupPrecision &prec,
2015                          const Vec3 &coord, const Vec2 &lodBounds, const Vec4 &result)
2016 {
2017     const float minLod        = lodBounds.x();
2018     const float maxLod        = lodBounds.y();
2019     const bool canBeMagnified = minLod <= sampler.lodThreshold;
2020     const bool canBeMinified  = maxLod > sampler.lodThreshold;
2021 
2022     DE_ASSERT(isSamplerSupported(sampler));
2023 
2024     if (canBeMagnified)
2025     {
2026         if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, result))
2027             return true;
2028     }
2029 
2030     if (canBeMinified)
2031     {
2032         const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
2033         const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
2034         const int minTexLevel      = 0;
2035         const int maxTexLevel      = texture.getNumLevels() - 1;
2036 
2037         DE_ASSERT(minTexLevel <= maxTexLevel);
2038 
2039         if (isLinearMipmap && minTexLevel < maxTexLevel)
2040         {
2041             const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
2042             const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
2043 
2044             DE_ASSERT(minLevel <= maxLevel);
2045 
2046             for (int level = minLevel; level <= maxLevel; level++)
2047             {
2048                 const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
2049                 const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
2050 
2051                 if (isMipmapLinearSampleResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler,
2052                                                     getLevelFilter(sampler.minFilter), prec, coord, Vec2(minF, maxF),
2053                                                     result))
2054                     return true;
2055             }
2056         }
2057         else if (isNearestMipmap)
2058         {
2059             // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
2060             //         decision to allow floor(lod + 0.5) as well.
2061             const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
2062             const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
2063 
2064             DE_ASSERT(minLevel <= maxLevel);
2065 
2066             for (int level = minLevel; level <= maxLevel; level++)
2067             {
2068                 if (isLevelSampleResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec,
2069                                              coord, result))
2070                     return true;
2071             }
2072         }
2073         else
2074         {
2075             if (isLevelSampleResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, result))
2076                 return true;
2077         }
2078     }
2079 
2080     return false;
2081 }
2082 
getCubeArrayLevelFaces(const TextureCubeArrayView & texture,const int levelNdx,const int layerNdx,ConstPixelBufferAccess (& out)[CUBEFACE_LAST])2083 static void getCubeArrayLevelFaces(const TextureCubeArrayView &texture, const int levelNdx, const int layerNdx,
2084                                    ConstPixelBufferAccess (&out)[CUBEFACE_LAST])
2085 {
2086     const ConstPixelBufferAccess &level = texture.getLevel(levelNdx);
2087     const int layerDepth                = layerNdx * 6;
2088 
2089     for (int faceNdx = 0; faceNdx < CUBEFACE_LAST; faceNdx++)
2090     {
2091         const CubeFace face = (CubeFace)faceNdx;
2092         out[faceNdx] =
2093             getSubregion(level, 0, 0, layerDepth + getCubeArrayFaceIndex(face), level.getWidth(), level.getHeight(), 1);
2094     }
2095 }
2096 
isLookupResultValid(const TextureCubeArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const IVec4 & coordBits,const Vec4 & coord,const Vec2 & lodBounds,const Vec4 & result)2097 bool isLookupResultValid(const TextureCubeArrayView &texture, const Sampler &sampler, const LookupPrecision &prec,
2098                          const IVec4 &coordBits, const Vec4 &coord, const Vec2 &lodBounds, const Vec4 &result)
2099 {
2100     const IVec2 layerRange = computeLayerRange(texture.getNumLayers(), coordBits.w(), coord.w());
2101     const Vec3 layerCoord  = coord.toWidth<3>();
2102     int numPossibleFaces   = 0;
2103     CubeFace possibleFaces[CUBEFACE_LAST];
2104 
2105     DE_ASSERT(isSamplerSupported(sampler));
2106 
2107     getPossibleCubeFaces(layerCoord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
2108 
2109     if (numPossibleFaces == 0)
2110         return true; // Result is undefined.
2111 
2112     for (int layerNdx = layerRange.x(); layerNdx <= layerRange.y(); layerNdx++)
2113     {
2114         for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
2115         {
2116             const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx],
2117                                                  projectToFace(possibleFaces[tryFaceNdx], layerCoord));
2118             const float minLod        = lodBounds.x();
2119             const float maxLod        = lodBounds.y();
2120             const bool canBeMagnified = minLod <= sampler.lodThreshold;
2121             const bool canBeMinified  = maxLod > sampler.lodThreshold;
2122 
2123             if (canBeMagnified)
2124             {
2125                 ConstPixelBufferAccess faces[CUBEFACE_LAST];
2126                 getCubeArrayLevelFaces(texture, 0, layerNdx, faces);
2127 
2128                 if (isCubeLevelSampleResultValid(faces, sampler, sampler.magFilter, prec, faceCoords, result))
2129                     return true;
2130             }
2131 
2132             if (canBeMinified)
2133             {
2134                 const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
2135                 const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
2136                 const int minTexLevel      = 0;
2137                 const int maxTexLevel      = texture.getNumLevels() - 1;
2138 
2139                 DE_ASSERT(minTexLevel <= maxTexLevel);
2140 
2141                 if (isLinearMipmap && minTexLevel < maxTexLevel)
2142                 {
2143                     const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
2144                     const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
2145 
2146                     DE_ASSERT(minLevel <= maxLevel);
2147 
2148                     for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
2149                     {
2150                         const float minF = de::clamp(minLod - float(levelNdx), 0.0f, 1.0f);
2151                         const float maxF = de::clamp(maxLod - float(levelNdx), 0.0f, 1.0f);
2152 
2153                         ConstPixelBufferAccess faces0[CUBEFACE_LAST];
2154                         ConstPixelBufferAccess faces1[CUBEFACE_LAST];
2155 
2156                         getCubeArrayLevelFaces(texture, levelNdx, layerNdx, faces0);
2157                         getCubeArrayLevelFaces(texture, levelNdx + 1, layerNdx, faces1);
2158 
2159                         if (isCubeMipmapLinearSampleResultValid(faces0, faces1, sampler,
2160                                                                 getLevelFilter(sampler.minFilter), prec, faceCoords,
2161                                                                 Vec2(minF, maxF), result))
2162                             return true;
2163                     }
2164                 }
2165                 else if (isNearestMipmap)
2166                 {
2167                     // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
2168                     //         decision to allow floor(lod + 0.5) as well.
2169                     const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
2170                     const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
2171 
2172                     DE_ASSERT(minLevel <= maxLevel);
2173 
2174                     for (int levelNdx = minLevel; levelNdx <= maxLevel; levelNdx++)
2175                     {
2176                         ConstPixelBufferAccess faces[CUBEFACE_LAST];
2177                         getCubeArrayLevelFaces(texture, levelNdx, layerNdx, faces);
2178 
2179                         if (isCubeLevelSampleResultValid(faces, sampler, getLevelFilter(sampler.minFilter), prec,
2180                                                          faceCoords, result))
2181                             return true;
2182                     }
2183                 }
2184                 else
2185                 {
2186                     ConstPixelBufferAccess faces[CUBEFACE_LAST];
2187                     getCubeArrayLevelFaces(texture, 0, layerNdx, faces);
2188 
2189                     if (isCubeLevelSampleResultValid(faces, sampler, sampler.minFilter, prec, faceCoords, result))
2190                         return true;
2191                 }
2192             }
2193         }
2194     }
2195 
2196     return false;
2197 }
2198 
computeFixedPointThreshold(const IVec4 & bits)2199 Vec4 computeFixedPointThreshold(const IVec4 &bits)
2200 {
2201     return computeFixedPointError(bits);
2202 }
2203 
computeFloatingPointThreshold(const IVec4 & bits,const Vec4 & value)2204 Vec4 computeFloatingPointThreshold(const IVec4 &bits, const Vec4 &value)
2205 {
2206     return computeFloatingPointError(value, bits);
2207 }
2208 
computeColorBitsThreshold(const IVec4 & bits,const IVec4 & numAccurateBits)2209 Vec4 computeColorBitsThreshold(const IVec4 &bits, const IVec4 &numAccurateBits)
2210 {
2211     return computeColorBitsError(bits, numAccurateBits);
2212 }
2213 
computeLodBoundsFromDerivates(const float dudx,const float dvdx,const float dwdx,const float dudy,const float dvdy,const float dwdy,const LodPrecision & prec)2214 Vec2 computeLodBoundsFromDerivates(const float dudx, const float dvdx, const float dwdx, const float dudy,
2215                                    const float dvdy, const float dwdy, const LodPrecision &prec)
2216 {
2217     const float mux = deFloatAbs(dudx);
2218     const float mvx = deFloatAbs(dvdx);
2219     const float mwx = deFloatAbs(dwdx);
2220     const float muy = deFloatAbs(dudy);
2221     const float mvy = deFloatAbs(dvdy);
2222     const float mwy = deFloatAbs(dwdy);
2223 
2224     // Ideal:
2225     // px = deFloatSqrt2(mux*mux + mvx*mvx + mwx*mwx);
2226     // py = deFloatSqrt2(muy*muy + mvy*mvy + mwy*mwy);
2227 
2228     // fx, fy estimate lower bounds
2229     const float fxMin = de::max(de::max(mux, mvx), mwx);
2230     const float fyMin = de::max(de::max(muy, mvy), mwy);
2231 
2232     // fx, fy estimate upper bounds
2233     const float sqrt2 = deFloatSqrt(2.0f);
2234     const float fxMax = sqrt2 * (mux + mvx + mwx);
2235     const float fyMax = sqrt2 * (muy + mvy + mwy);
2236 
2237     // p = max(px, py) (isotropic filtering)
2238     const float pMin = de::max(fxMin, fyMin);
2239     const float pMax = de::max(fxMax, fyMax);
2240 
2241     // error terms
2242     const float pMinErr = computeFloatingPointError(pMin, prec.derivateBits);
2243     const float pMaxErr = computeFloatingPointError(pMax, prec.derivateBits);
2244 
2245     const float minLod = deFloatLog2(pMin - pMinErr);
2246     const float maxLod = deFloatLog2(pMax + pMaxErr);
2247     const float lodErr = computeFixedPointError(prec.lodBits);
2248 
2249     DE_ASSERT(minLod <= maxLod);
2250     return Vec2(minLod - lodErr, maxLod + lodErr);
2251 }
2252 
computeLodBoundsFromDerivates(const float dudx,const float dvdx,const float dudy,const float dvdy,const LodPrecision & prec)2253 Vec2 computeLodBoundsFromDerivates(const float dudx, const float dvdx, const float dudy, const float dvdy,
2254                                    const LodPrecision &prec)
2255 {
2256     return computeLodBoundsFromDerivates(dudx, dvdx, 0.0f, dudy, dvdy, 0.0f, prec);
2257 }
2258 
computeLodBoundsFromDerivates(const float dudx,const float dudy,const LodPrecision & prec)2259 Vec2 computeLodBoundsFromDerivates(const float dudx, const float dudy, const LodPrecision &prec)
2260 {
2261     return computeLodBoundsFromDerivates(dudx, 0.0f, 0.0f, dudy, 0.0f, 0.0f, prec);
2262 }
2263 
computeCubeLodBoundsFromDerivates(const Vec3 & coord,const Vec3 & coordDx,const Vec3 & coordDy,const int faceSize,const LodPrecision & prec)2264 Vec2 computeCubeLodBoundsFromDerivates(const Vec3 &coord, const Vec3 &coordDx, const Vec3 &coordDy, const int faceSize,
2265                                        const LodPrecision &prec)
2266 {
2267     const bool allowBrokenEdgeDerivate = false;
2268     const CubeFace face                = selectCubeFace(coord);
2269     int maNdx                          = 0;
2270     int sNdx                           = 0;
2271     int tNdx                           = 0;
2272 
2273     // \note Derivate signs don't matter when computing lod
2274     switch (face)
2275     {
2276     case CUBEFACE_NEGATIVE_X:
2277     case CUBEFACE_POSITIVE_X:
2278         maNdx = 0;
2279         sNdx  = 2;
2280         tNdx  = 1;
2281         break;
2282     case CUBEFACE_NEGATIVE_Y:
2283     case CUBEFACE_POSITIVE_Y:
2284         maNdx = 1;
2285         sNdx  = 0;
2286         tNdx  = 2;
2287         break;
2288     case CUBEFACE_NEGATIVE_Z:
2289     case CUBEFACE_POSITIVE_Z:
2290         maNdx = 2;
2291         sNdx  = 0;
2292         tNdx  = 1;
2293         break;
2294     default:
2295         DE_ASSERT(false);
2296     }
2297 
2298     {
2299         const float sc    = coord[sNdx];
2300         const float tc    = coord[tNdx];
2301         const float ma    = de::abs(coord[maNdx]);
2302         const float scdx  = coordDx[sNdx];
2303         const float tcdx  = coordDx[tNdx];
2304         const float madx  = de::abs(coordDx[maNdx]);
2305         const float scdy  = coordDy[sNdx];
2306         const float tcdy  = coordDy[tNdx];
2307         const float mady  = de::abs(coordDy[maNdx]);
2308         const float dudx  = float(faceSize) * 0.5f * (scdx * ma - sc * madx) / (ma * ma);
2309         const float dvdx  = float(faceSize) * 0.5f * (tcdx * ma - tc * madx) / (ma * ma);
2310         const float dudy  = float(faceSize) * 0.5f * (scdy * ma - sc * mady) / (ma * ma);
2311         const float dvdy  = float(faceSize) * 0.5f * (tcdy * ma - tc * mady) / (ma * ma);
2312         const Vec2 bounds = computeLodBoundsFromDerivates(dudx, dvdx, dudy, dvdy, prec);
2313 
2314         // Implementations may compute derivate from projected (s, t) resulting in incorrect values at edges.
2315         if (allowBrokenEdgeDerivate)
2316         {
2317             const Vec3 dxErr = computeFloatingPointError(coordDx, IVec3(prec.derivateBits));
2318             const Vec3 dyErr = computeFloatingPointError(coordDy, IVec3(prec.derivateBits));
2319             const Vec3 xoffs = abs(coordDx) + dxErr;
2320             const Vec3 yoffs = abs(coordDy) + dyErr;
2321 
2322             if (selectCubeFace(coord + xoffs) != face || selectCubeFace(coord - xoffs) != face ||
2323                 selectCubeFace(coord + yoffs) != face || selectCubeFace(coord - yoffs) != face)
2324             {
2325                 return Vec2(bounds.x(), 1000.0f);
2326             }
2327         }
2328 
2329         return bounds;
2330     }
2331 }
2332 
clampLodBounds(const Vec2 & lodBounds,const Vec2 & lodMinMax,const LodPrecision & prec)2333 Vec2 clampLodBounds(const Vec2 &lodBounds, const Vec2 &lodMinMax, const LodPrecision &prec)
2334 {
2335     const float lodErr = computeFixedPointError(prec.lodBits);
2336     const float a      = lodMinMax.x();
2337     const float b      = lodMinMax.y();
2338     return Vec2(de::clamp(lodBounds.x(), a - lodErr, b - lodErr), de::clamp(lodBounds.y(), a + lodErr, b + lodErr));
2339 }
2340 
isLevel1DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const LookupPrecision & prec,const float coordX,const int coordY,const Vec4 & result)2341 bool isLevel1DLookupResultValid(const ConstPixelBufferAccess &access, const Sampler &sampler,
2342                                 TexLookupScaleMode scaleMode, const LookupPrecision &prec, const float coordX,
2343                                 const int coordY, const Vec4 &result)
2344 {
2345     const Sampler::FilterMode filterMode =
2346         scaleMode == TEX_LOOKUP_SCALE_MAGNIFY ? sampler.magFilter : sampler.minFilter;
2347     return isLevelSampleResultValid(access, sampler, filterMode, prec, coordX, coordY, result);
2348 }
2349 
isLevel1DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const float coordX,const int coordY,const IVec4 & result)2350 bool isLevel1DLookupResultValid(const ConstPixelBufferAccess &access, const Sampler &sampler,
2351                                 TexLookupScaleMode scaleMode, const IntLookupPrecision &prec, const float coordX,
2352                                 const int coordY, const IVec4 &result)
2353 {
2354     DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
2355     DE_UNREF(scaleMode);
2356     return isNearestSampleResultValid(access, sampler, prec, coordX, coordY, result);
2357 }
2358 
isLevel1DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const float coordX,const int coordY,const UVec4 & result)2359 bool isLevel1DLookupResultValid(const ConstPixelBufferAccess &access, const Sampler &sampler,
2360                                 TexLookupScaleMode scaleMode, const IntLookupPrecision &prec, const float coordX,
2361                                 const int coordY, const UVec4 &result)
2362 {
2363     DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
2364     DE_UNREF(scaleMode);
2365     return isNearestSampleResultValid(access, sampler, prec, coordX, coordY, result);
2366 }
2367 
isLevel2DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const LookupPrecision & prec,const Vec2 & coord,const int coordZ,const Vec4 & result)2368 bool isLevel2DLookupResultValid(const ConstPixelBufferAccess &access, const Sampler &sampler,
2369                                 TexLookupScaleMode scaleMode, const LookupPrecision &prec, const Vec2 &coord,
2370                                 const int coordZ, const Vec4 &result)
2371 {
2372     const Sampler::FilterMode filterMode =
2373         scaleMode == TEX_LOOKUP_SCALE_MAGNIFY ? sampler.magFilter : sampler.minFilter;
2374     return isLevelSampleResultValid(access, sampler, filterMode, prec, coord, coordZ, result);
2375 }
2376 
isLevel2DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec2 & coord,const int coordZ,const IVec4 & result)2377 bool isLevel2DLookupResultValid(const ConstPixelBufferAccess &access, const Sampler &sampler,
2378                                 TexLookupScaleMode scaleMode, const IntLookupPrecision &prec, const Vec2 &coord,
2379                                 const int coordZ, const IVec4 &result)
2380 {
2381     DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
2382     DE_UNREF(scaleMode);
2383     return isNearestSampleResultValid(access, sampler, prec, coord, coordZ, result);
2384 }
2385 
isLevel2DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec2 & coord,const int coordZ,const UVec4 & result)2386 bool isLevel2DLookupResultValid(const ConstPixelBufferAccess &access, const Sampler &sampler,
2387                                 TexLookupScaleMode scaleMode, const IntLookupPrecision &prec, const Vec2 &coord,
2388                                 const int coordZ, const UVec4 &result)
2389 {
2390     DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
2391     DE_UNREF(scaleMode);
2392     return isNearestSampleResultValid(access, sampler, prec, coord, coordZ, result);
2393 }
2394 
isLevel3DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const LookupPrecision & prec,const Vec3 & coord,const Vec4 & result)2395 bool isLevel3DLookupResultValid(const ConstPixelBufferAccess &access, const Sampler &sampler,
2396                                 TexLookupScaleMode scaleMode, const LookupPrecision &prec, const Vec3 &coord,
2397                                 const Vec4 &result)
2398 {
2399     const tcu::Sampler::FilterMode filterMode =
2400         scaleMode == TEX_LOOKUP_SCALE_MAGNIFY ? sampler.magFilter : sampler.minFilter;
2401     return isLevelSampleResultValid(access, sampler, filterMode, prec, coord, result);
2402 }
2403 
isLevel3DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec3 & coord,const IVec4 & result)2404 bool isLevel3DLookupResultValid(const ConstPixelBufferAccess &access, const Sampler &sampler,
2405                                 TexLookupScaleMode scaleMode, const IntLookupPrecision &prec, const Vec3 &coord,
2406                                 const IVec4 &result)
2407 {
2408     DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
2409     DE_UNREF(scaleMode);
2410     return isNearestSampleResultValid(access, sampler, prec, coord, result);
2411 }
2412 
isLevel3DLookupResultValid(const ConstPixelBufferAccess & access,const Sampler & sampler,TexLookupScaleMode scaleMode,const IntLookupPrecision & prec,const Vec3 & coord,const UVec4 & result)2413 bool isLevel3DLookupResultValid(const ConstPixelBufferAccess &access, const Sampler &sampler,
2414                                 TexLookupScaleMode scaleMode, const IntLookupPrecision &prec, const Vec3 &coord,
2415                                 const UVec4 &result)
2416 {
2417     DE_ASSERT(sampler.minFilter == Sampler::NEAREST && sampler.magFilter == Sampler::NEAREST);
2418     DE_UNREF(scaleMode);
2419     return isNearestSampleResultValid(access, sampler, prec, coord, result);
2420 }
2421 
2422 template <typename PrecType, typename ScalarType>
isGatherOffsetsResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const PrecType & prec,const Vec2 & coord,int coordZ,int componentNdx,const IVec2 (& offsets)[4],const Vector<ScalarType,4> & result)2423 static bool isGatherOffsetsResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
2424                                        const PrecType &prec, const Vec2 &coord, int coordZ, int componentNdx,
2425                                        const IVec2 (&offsets)[4], const Vector<ScalarType, 4> &result)
2426 {
2427     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(),
2428                                                          prec.coordBits.x(), prec.uvwBits.x());
2429     const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(),
2430                                                          prec.coordBits.y(), prec.uvwBits.y());
2431 
2432     // Integer coordinate bounds for (x0, y0) - without wrap mode
2433     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
2434     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
2435     const int minJ = deFloorFloatToInt32(vBounds.x() - 0.5f);
2436     const int maxJ = deFloorFloatToInt32(vBounds.y() - 0.5f);
2437 
2438     const int w = level.getWidth();
2439     const int h = level.getHeight();
2440 
2441     for (int j = minJ; j <= maxJ; j++)
2442     {
2443         for (int i = minI; i <= maxI; i++)
2444         {
2445             Vector<ScalarType, 4> color;
2446             for (int offNdx = 0; offNdx < 4; offNdx++)
2447             {
2448                 // offNdx-th coordinate offset and then wrapped.
2449                 const int x   = wrap(sampler.wrapS, i + offsets[offNdx].x(), w);
2450                 const int y   = wrap(sampler.wrapT, j + offsets[offNdx].y(), h);
2451                 color[offNdx] = lookup<ScalarType>(level, sampler, x, y, coordZ)[componentNdx];
2452             }
2453 
2454             if (isColorValid(prec, color, result))
2455                 return true;
2456         }
2457     }
2458 
2459     return false;
2460 }
2461 
isGatherOffsetsResultValid(const Texture2DView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec2 & coord,int componentNdx,const IVec2 (& offsets)[4],const Vec4 & result)2462 bool isGatherOffsetsResultValid(const Texture2DView &texture, const Sampler &sampler, const LookupPrecision &prec,
2463                                 const Vec2 &coord, int componentNdx, const IVec2 (&offsets)[4], const Vec4 &result)
2464 {
2465     return isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord, 0, componentNdx, offsets, result);
2466 }
2467 
isGatherOffsetsResultValid(const Texture2DView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec2 & coord,int componentNdx,const IVec2 (& offsets)[4],const IVec4 & result)2468 bool isGatherOffsetsResultValid(const Texture2DView &texture, const Sampler &sampler, const IntLookupPrecision &prec,
2469                                 const Vec2 &coord, int componentNdx, const IVec2 (&offsets)[4], const IVec4 &result)
2470 {
2471     return isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord, 0, componentNdx, offsets, result);
2472 }
2473 
isGatherOffsetsResultValid(const Texture2DView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec2 & coord,int componentNdx,const IVec2 (& offsets)[4],const UVec4 & result)2474 bool isGatherOffsetsResultValid(const Texture2DView &texture, const Sampler &sampler, const IntLookupPrecision &prec,
2475                                 const Vec2 &coord, int componentNdx, const IVec2 (&offsets)[4], const UVec4 &result)
2476 {
2477     return isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord, 0, componentNdx, offsets, result);
2478 }
2479 
2480 template <typename PrecType, typename ScalarType>
is2DArrayGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const PrecType & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const Vector<ScalarType,4> & result)2481 static bool is2DArrayGatherOffsetsResultValid(const Texture2DArrayView &texture, const Sampler &sampler,
2482                                               const PrecType &prec, const Vec3 &coord, int componentNdx,
2483                                               const IVec2 (&offsets)[4], const Vector<ScalarType, 4> &result)
2484 {
2485     const IVec2 layerRange = computeLayerRange(texture.getNumLayers(), prec.coordBits.z(), coord.z());
2486     for (int layer = layerRange.x(); layer <= layerRange.y(); layer++)
2487     {
2488         if (isGatherOffsetsResultValid(texture.getLevel(0), sampler, prec, coord.swizzle(0, 1), layer, componentNdx,
2489                                        offsets, result))
2490             return true;
2491     }
2492     return false;
2493 }
2494 
isGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const Vec4 & result)2495 bool isGatherOffsetsResultValid(const Texture2DArrayView &texture, const Sampler &sampler, const LookupPrecision &prec,
2496                                 const Vec3 &coord, int componentNdx, const IVec2 (&offsets)[4], const Vec4 &result)
2497 {
2498     return is2DArrayGatherOffsetsResultValid(texture, sampler, prec, coord, componentNdx, offsets, result);
2499 }
2500 
isGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const IVec4 & result)2501 bool isGatherOffsetsResultValid(const Texture2DArrayView &texture, const Sampler &sampler,
2502                                 const IntLookupPrecision &prec, const Vec3 &coord, int componentNdx,
2503                                 const IVec2 (&offsets)[4], const IVec4 &result)
2504 {
2505     return is2DArrayGatherOffsetsResultValid(texture, sampler, prec, coord, componentNdx, offsets, result);
2506 }
2507 
isGatherOffsetsResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec2 (& offsets)[4],const UVec4 & result)2508 bool isGatherOffsetsResultValid(const Texture2DArrayView &texture, const Sampler &sampler,
2509                                 const IntLookupPrecision &prec, const Vec3 &coord, int componentNdx,
2510                                 const IVec2 (&offsets)[4], const UVec4 &result)
2511 {
2512     return is2DArrayGatherOffsetsResultValid(texture, sampler, prec, coord, componentNdx, offsets, result);
2513 }
2514 
2515 template <typename PrecType, typename ScalarType>
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const PrecType & prec,const CubeFaceFloatCoords & coords,int componentNdx,const Vector<ScalarType,4> & result)2516 static bool isGatherResultValid(const TextureCubeView &texture, const Sampler &sampler, const PrecType &prec,
2517                                 const CubeFaceFloatCoords &coords, int componentNdx,
2518                                 const Vector<ScalarType, 4> &result)
2519 {
2520     const int size = texture.getLevelFace(0, coords.face).getWidth();
2521 
2522     const Vec2 uBounds =
2523         computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
2524     const Vec2 vBounds =
2525         computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
2526 
2527     // Integer coordinate bounds for (x0,y0) - without wrap mode
2528     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
2529     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
2530     const int minJ = deFloorFloatToInt32(vBounds.x() - 0.5f);
2531     const int maxJ = deFloorFloatToInt32(vBounds.y() - 0.5f);
2532 
2533     // Face accesses
2534     ConstPixelBufferAccess faces[CUBEFACE_LAST];
2535     for (int face = 0; face < CUBEFACE_LAST; face++)
2536         faces[face] = texture.getLevelFace(0, CubeFace(face));
2537 
2538     for (int j = minJ; j <= maxJ; j++)
2539     {
2540         for (int i = minI; i <= maxI; i++)
2541         {
2542             static const IVec2 offsets[4] = {IVec2(0, 1), IVec2(1, 1), IVec2(1, 0), IVec2(0, 0)};
2543 
2544             Vector<ScalarType, 4> color;
2545             for (int offNdx = 0; offNdx < 4; offNdx++)
2546             {
2547                 const CubeFaceIntCoords c = remapCubeEdgeCoords(
2548                     CubeFaceIntCoords(coords.face, i + offsets[offNdx].x(), j + offsets[offNdx].y()), size);
2549                 // If any of samples is out of both edges, implementations can do pretty much anything according to spec.
2550                 // \todo [2014-06-05 nuutti] Test the special case where all corner pixels have exactly the same color.
2551                 //                             See also isSeamlessLinearSampleResultValid and similar.
2552                 if (c.face == CUBEFACE_LAST)
2553                     return true;
2554 
2555                 color[offNdx] = lookup<ScalarType>(faces[c.face], sampler, c.s, c.t, 0)[componentNdx];
2556             }
2557 
2558             if (isColorValid(prec, color, result))
2559                 return true;
2560         }
2561     }
2562 
2563     return false;
2564 }
2565 
2566 template <typename PrecType, typename ScalarType>
isCubeGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const PrecType & prec,const Vec3 & coord,int componentNdx,const Vector<ScalarType,4> & result)2567 static bool isCubeGatherResultValid(const TextureCubeView &texture, const Sampler &sampler, const PrecType &prec,
2568                                     const Vec3 &coord, int componentNdx, const Vector<ScalarType, 4> &result)
2569 {
2570     int numPossibleFaces = 0;
2571     CubeFace possibleFaces[CUBEFACE_LAST];
2572 
2573     getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
2574 
2575     if (numPossibleFaces == 0)
2576         return true; // Result is undefined.
2577 
2578     for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
2579     {
2580         const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx],
2581                                              projectToFace(possibleFaces[tryFaceNdx], coord));
2582 
2583         if (isGatherResultValid(texture, sampler, prec, faceCoords, componentNdx, result))
2584             return true;
2585     }
2586 
2587     return false;
2588 }
2589 
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const LookupPrecision & prec,const Vec3 & coord,int componentNdx,const Vec4 & result)2590 bool isGatherResultValid(const TextureCubeView &texture, const Sampler &sampler, const LookupPrecision &prec,
2591                          const Vec3 &coord, int componentNdx, const Vec4 &result)
2592 {
2593     return isCubeGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
2594 }
2595 
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const IVec4 & result)2596 bool isGatherResultValid(const TextureCubeView &texture, const Sampler &sampler, const IntLookupPrecision &prec,
2597                          const Vec3 &coord, int componentNdx, const IVec4 &result)
2598 {
2599     return isCubeGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
2600 }
2601 
isGatherResultValid(const TextureCubeView & texture,const Sampler & sampler,const IntLookupPrecision & prec,const Vec3 & coord,int componentNdx,const UVec4 & result)2602 bool isGatherResultValid(const TextureCubeView &texture, const Sampler &sampler, const IntLookupPrecision &prec,
2603                          const Vec3 &coord, int componentNdx, const UVec4 &result)
2604 {
2605     return isCubeGatherResultValid(texture, sampler, prec, coord, componentNdx, result);
2606 }
2607 
2608 } // namespace tcu
2609