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