xref: /aosp_15_r20/external/deqp/framework/common/tcuTexCompareVerifier.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Tester Core
3  * ----------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Texture compare (shadow) result verifier.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "tcuTexCompareVerifier.hpp"
25 #include "tcuTexVerifierUtil.hpp"
26 #include "tcuTextureUtil.hpp"
27 #include "tcuVectorUtil.hpp"
28 #include "deMath.h"
29 
30 namespace tcu
31 {
32 
33 using namespace TexVerifierUtil;
34 
35 // Generic utilities
36 
37 #if defined(DE_DEBUG)
isSamplerSupported(const Sampler & sampler)38 static bool isSamplerSupported(const Sampler &sampler)
39 {
40     return sampler.compare != Sampler::COMPAREMODE_NONE && isWrapModeSupported(sampler.wrapS) &&
41            isWrapModeSupported(sampler.wrapT) && isWrapModeSupported(sampler.wrapR);
42 }
43 #endif // DE_DEBUG
44 
45 struct CmpResultSet
46 {
47     bool isTrue;
48     bool isFalse;
49 
CmpResultSettcu::CmpResultSet50     CmpResultSet(void) : isTrue(false), isFalse(false)
51     {
52     }
53 };
54 
execCompare(const Sampler::CompareMode compareMode,const float cmpValue_,const float cmpReference_,const int referenceBits,const bool isFixedPoint)55 static CmpResultSet execCompare(const Sampler::CompareMode compareMode, const float cmpValue_,
56                                 const float cmpReference_, const int referenceBits, const bool isFixedPoint)
57 {
58     const bool clampValues =
59         isFixedPoint; // if comparing against a floating point texture, ref (and value) is not clamped
60     const float cmpValue     = (clampValues) ? (de::clamp(cmpValue_, 0.0f, 1.0f)) : (cmpValue_);
61     const float cmpReference = (clampValues) ? (de::clamp(cmpReference_, 0.0f, 1.0f)) : (cmpReference_);
62     const float err          = computeFixedPointError(referenceBits);
63     CmpResultSet res;
64 
65     switch (compareMode)
66     {
67     case Sampler::COMPAREMODE_LESS:
68         res.isTrue  = cmpReference - err < cmpValue;
69         res.isFalse = cmpReference + err >= cmpValue;
70         break;
71 
72     case Sampler::COMPAREMODE_LESS_OR_EQUAL:
73         res.isTrue  = cmpReference - err <= cmpValue;
74         res.isFalse = cmpReference + err > cmpValue;
75         break;
76 
77     case Sampler::COMPAREMODE_GREATER:
78         res.isTrue  = cmpReference + err > cmpValue;
79         res.isFalse = cmpReference - err <= cmpValue;
80         break;
81 
82     case Sampler::COMPAREMODE_GREATER_OR_EQUAL:
83         res.isTrue  = cmpReference + err >= cmpValue;
84         res.isFalse = cmpReference - err < cmpValue;
85         break;
86 
87     case Sampler::COMPAREMODE_EQUAL:
88         res.isTrue  = de::inRange(cmpValue, cmpReference - err, cmpReference + err);
89         res.isFalse = err != 0.0f || cmpValue != cmpReference;
90         break;
91 
92     case Sampler::COMPAREMODE_NOT_EQUAL:
93         res.isTrue  = err != 0.0f || cmpValue != cmpReference;
94         res.isFalse = de::inRange(cmpValue, cmpReference - err, cmpReference + err);
95         break;
96 
97     case Sampler::COMPAREMODE_ALWAYS:
98         res.isTrue = true;
99         break;
100 
101     case Sampler::COMPAREMODE_NEVER:
102         res.isFalse = true;
103         break;
104 
105     default:
106         DE_ASSERT(false);
107     }
108 
109     DE_ASSERT(res.isTrue || res.isFalse);
110     return res;
111 }
112 
isResultInSet(const CmpResultSet resultSet,const float result,const int resultBits)113 static inline bool isResultInSet(const CmpResultSet resultSet, const float result, const int resultBits)
114 {
115     const float err  = computeFixedPointError(resultBits);
116     const float minR = result - err;
117     const float maxR = result + err;
118 
119     return (resultSet.isTrue && de::inRange(1.0f, minR, maxR)) || (resultSet.isFalse && de::inRange(0.0f, minR, maxR));
120 }
121 
coordsInBounds(const ConstPixelBufferAccess & access,int x,int y,int z)122 static inline bool coordsInBounds(const ConstPixelBufferAccess &access, int x, int y, int z)
123 {
124     return de::inBounds(x, 0, access.getWidth()) && de::inBounds(y, 0, access.getHeight()) &&
125            de::inBounds(z, 0, access.getDepth());
126 }
127 
128 // lookup depth value at a point that is guaranteed to not sample border such as cube map faces.
lookupDepthNoBorder(const tcu::ConstPixelBufferAccess & access,const Sampler & sampler,int i,int j,int k=0)129 static float lookupDepthNoBorder(const tcu::ConstPixelBufferAccess &access, const Sampler &sampler, int i, int j,
130                                  int k = 0)
131 {
132     DE_UNREF(sampler);
133     DE_ASSERT(coordsInBounds(access, i, j, k));
134     DE_ASSERT(access.getFormat().order == TextureFormat::D || access.getFormat().order == TextureFormat::DS ||
135               access.getFormat().order == TextureFormat::R);
136 
137     if (access.getFormat().order == TextureFormat::R)
138         return access.getPixel(i, j, k).x();
139     else
140         return access.getPixDepth(i, j, k);
141 }
142 
lookupDepth(const tcu::ConstPixelBufferAccess & access,const Sampler & sampler,int i,int j,int k)143 static float lookupDepth(const tcu::ConstPixelBufferAccess &access, const Sampler &sampler, int i, int j, int k)
144 {
145     if (coordsInBounds(access, i, j, k))
146         return lookupDepthNoBorder(access, sampler, i, j, k);
147     else
148         return sampleTextureBorder<float>(access.getFormat(), sampler).x();
149 }
150 
151 // Values are in order (0,0), (1,0), (0,1), (1,1)
bilinearInterpolate(const Vec4 & values,const float x,const float y)152 static float bilinearInterpolate(const Vec4 &values, const float x, const float y)
153 {
154     const float v00 = values[0];
155     const float v10 = values[1];
156     const float v01 = values[2];
157     const float v11 = values[3];
158     const float res = v00 * (1.0f - x) * (1.0f - y) + v10 * x * (1.0f - y) + v01 * (1.0f - x) * y + v11 * x * y;
159     return res;
160 }
161 
isFixedPointDepthTextureFormat(const tcu::TextureFormat & format)162 static bool isFixedPointDepthTextureFormat(const tcu::TextureFormat &format)
163 {
164     const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(format.type);
165 
166     if (format.order == TextureFormat::D || format.order == TextureFormat::R)
167     {
168         // depth internal formats cannot be non-normalized integers
169         return channelClass != tcu::TEXTURECHANNELCLASS_FLOATING_POINT;
170     }
171     else if (format.order == TextureFormat::DS)
172     {
173         // combined formats have no single channel class, detect format manually
174         switch (format.type)
175         {
176         case tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV:
177             return false;
178         case tcu::TextureFormat::UNSIGNED_INT_16_8_8:
179             return true;
180         case tcu::TextureFormat::UNSIGNED_INT_24_8:
181             return true;
182         case tcu::TextureFormat::UNSIGNED_INT_24_8_REV:
183             return true;
184 
185         default:
186         {
187             // unknown format
188             DE_ASSERT(false);
189             return true;
190         }
191         }
192     }
193 
194     return false;
195 }
196 
isLinearCompareValid(const Sampler::CompareMode compareMode,const TexComparePrecision & prec,const Vec2 & depths,const Vec2 & fBounds,const float cmpReference,const float result,const bool isFixedPointDepth)197 static bool isLinearCompareValid(const Sampler::CompareMode compareMode, const TexComparePrecision &prec,
198                                  const Vec2 &depths, const Vec2 &fBounds, const float cmpReference, const float result,
199                                  const bool isFixedPointDepth)
200 {
201     DE_ASSERT(0.0f <= fBounds.x() && fBounds.x() <= fBounds.y() && fBounds.y() <= 1.0f);
202 
203     const float d0 = depths[0];
204     const float d1 = depths[1];
205 
206     const CmpResultSet cmp0 = execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth);
207     const CmpResultSet cmp1 = execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth);
208 
209     const uint32_t isTrue  = (uint32_t(cmp0.isTrue) << 0) | (uint32_t(cmp1.isTrue) << 1);
210     const uint32_t isFalse = (uint32_t(cmp0.isFalse) << 0) | (uint32_t(cmp1.isFalse) << 1);
211 
212     // Interpolation parameters
213     const float f0 = fBounds.x();
214     const float f1 = fBounds.y();
215 
216     // Error parameters
217     const float pcfErr   = computeFixedPointError(prec.pcfBits);
218     const float resErr   = computeFixedPointError(prec.resultBits);
219     const float totalErr = pcfErr + resErr;
220 
221     // Iterate over all valid combinations.
222     for (uint32_t comb = 0; comb < (1 << 2); comb++)
223     {
224         // Filter out invalid combinations.
225         if (((comb & isTrue) | (~comb & isFalse)) != (1 << 2) - 1)
226             continue;
227 
228         const bool cmp0True = ((comb >> 0) & 1) != 0;
229         const bool cmp1True = ((comb >> 1) & 1) != 0;
230 
231         const float ref0 = cmp0True ? 1.0f : 0.0f;
232         const float ref1 = cmp1True ? 1.0f : 0.0f;
233 
234         const float v0   = ref0 * (1.0f - f0) + ref1 * f0;
235         const float v1   = ref0 * (1.0f - f1) + ref1 * f1;
236         const float minV = de::min(v0, v1);
237         const float maxV = de::max(v0, v1);
238         const float minR = minV - totalErr;
239         const float maxR = maxV + totalErr;
240 
241         if (de::inRange(result, minR, maxR))
242             return true;
243     }
244 
245     return false;
246 }
247 
extractBVec4(const uint32_t val,int offset)248 static inline BVec4 extractBVec4(const uint32_t val, int offset)
249 {
250     return BVec4(((val >> (offset + 0)) & 1) != 0, ((val >> (offset + 1)) & 1) != 0, ((val >> (offset + 2)) & 1) != 0,
251                  ((val >> (offset + 3)) & 1) != 0);
252 }
253 
isBilinearAnyCompareValid(const Sampler::CompareMode compareMode,const TexComparePrecision & prec,const Vec4 & depths,const float cmpReference,const float result,const bool isFixedPointDepth)254 static bool isBilinearAnyCompareValid(const Sampler::CompareMode compareMode, const TexComparePrecision &prec,
255                                       const Vec4 &depths, const float cmpReference, const float result,
256                                       const bool isFixedPointDepth)
257 {
258     DE_ASSERT(prec.pcfBits == 0);
259 
260     const float d0 = depths[0];
261     const float d1 = depths[1];
262     const float d2 = depths[2];
263     const float d3 = depths[3];
264 
265     const CmpResultSet cmp0 = execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth);
266     const CmpResultSet cmp1 = execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth);
267     const CmpResultSet cmp2 = execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth);
268     const CmpResultSet cmp3 = execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth);
269 
270     const bool canBeTrue  = cmp0.isTrue || cmp1.isTrue || cmp2.isTrue || cmp3.isTrue;
271     const bool canBeFalse = cmp0.isFalse || cmp1.isFalse || cmp2.isFalse || cmp3.isFalse;
272 
273     const float resErr = computeFixedPointError(prec.resultBits);
274 
275     const float minBound = canBeFalse ? 0.0f : 1.0f;
276     const float maxBound = canBeTrue ? 1.0f : 0.0f;
277 
278     return de::inRange(result, minBound - resErr, maxBound + resErr);
279 }
280 
isBilinearPCFCompareValid(const Sampler::CompareMode compareMode,const TexComparePrecision & prec,const Vec4 & depths,const Vec2 & xBounds,const Vec2 & yBounds,const float cmpReference,const float result,const bool isFixedPointDepth)281 static bool isBilinearPCFCompareValid(const Sampler::CompareMode compareMode, const TexComparePrecision &prec,
282                                       const Vec4 &depths, const Vec2 &xBounds, const Vec2 &yBounds,
283                                       const float cmpReference, const float result, const bool isFixedPointDepth)
284 {
285     DE_ASSERT(0.0f <= xBounds.x() && xBounds.x() <= xBounds.y() && xBounds.y() <= 1.0f);
286     DE_ASSERT(0.0f <= yBounds.x() && yBounds.x() <= yBounds.y() && yBounds.y() <= 1.0f);
287     DE_ASSERT(prec.pcfBits > 0);
288 
289     const float d0 = depths[0];
290     const float d1 = depths[1];
291     const float d2 = depths[2];
292     const float d3 = depths[3];
293 
294     const CmpResultSet cmp0 = execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth);
295     const CmpResultSet cmp1 = execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth);
296     const CmpResultSet cmp2 = execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth);
297     const CmpResultSet cmp3 = execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth);
298 
299     const uint32_t isTrue = (uint32_t(cmp0.isTrue) << 0) | (uint32_t(cmp1.isTrue) << 1) | (uint32_t(cmp2.isTrue) << 2) |
300                             (uint32_t(cmp3.isTrue) << 3);
301     const uint32_t isFalse = (uint32_t(cmp0.isFalse) << 0) | (uint32_t(cmp1.isFalse) << 1) |
302                              (uint32_t(cmp2.isFalse) << 2) | (uint32_t(cmp3.isFalse) << 3);
303 
304     // Interpolation parameters
305     const float x0 = xBounds.x();
306     const float x1 = xBounds.y();
307     const float y0 = yBounds.x();
308     const float y1 = yBounds.y();
309 
310     // Error parameters
311     const float pcfErr   = computeFixedPointError(prec.pcfBits);
312     const float resErr   = computeFixedPointError(prec.resultBits);
313     const float totalErr = pcfErr + resErr;
314 
315     // Iterate over all valid combinations.
316     // \note It is not enough to compute minmax over all possible result sets, as ranges may
317     //         not necessarily overlap, i.e. there are gaps between valid ranges.
318     for (uint32_t comb = 0; comb < (1 << 4); comb++)
319     {
320         // Filter out invalid combinations:
321         //  1) True bit is set in comb but not in isTrue => sample can not be true
322         //  2) True bit is NOT set in comb and not in isFalse => sample can not be false
323         if (((comb & isTrue) | (~comb & isFalse)) != (1 << 4) - 1)
324             continue;
325 
326         const BVec4 cmpTrue = extractBVec4(comb, 0);
327         const Vec4 refVal   = select(Vec4(1.0f), Vec4(0.0f), cmpTrue);
328 
329         const float v0   = bilinearInterpolate(refVal, x0, y0);
330         const float v1   = bilinearInterpolate(refVal, x1, y0);
331         const float v2   = bilinearInterpolate(refVal, x0, y1);
332         const float v3   = bilinearInterpolate(refVal, x1, y1);
333         const float minV = de::min(v0, de::min(v1, de::min(v2, v3)));
334         const float maxV = de::max(v0, de::max(v1, de::max(v2, v3)));
335         const float minR = minV - totalErr;
336         const float maxR = maxV + totalErr;
337 
338         if (de::inRange(result, minR, maxR))
339             return true;
340     }
341 
342     return false;
343 }
344 
isBilinearCompareValid(const Sampler::CompareMode compareMode,const TexComparePrecision & prec,const Vec4 & depths,const Vec2 & xBounds,const Vec2 & yBounds,const float cmpReference,const float result,const bool isFixedPointDepth)345 static bool isBilinearCompareValid(const Sampler::CompareMode compareMode, const TexComparePrecision &prec,
346                                    const Vec4 &depths, const Vec2 &xBounds, const Vec2 &yBounds,
347                                    const float cmpReference, const float result, const bool isFixedPointDepth)
348 {
349     if (prec.pcfBits > 0)
350         return isBilinearPCFCompareValid(compareMode, prec, depths, xBounds, yBounds, cmpReference, result,
351                                          isFixedPointDepth);
352     else
353         return isBilinearAnyCompareValid(compareMode, prec, depths, cmpReference, result, isFixedPointDepth);
354 }
355 
isTrilinearAnyCompareValid(const Sampler::CompareMode compareMode,const TexComparePrecision & prec,const Vec4 & depths0,const Vec4 & depths1,const float cmpReference,const float result,const bool isFixedPointDepth)356 static bool isTrilinearAnyCompareValid(const Sampler::CompareMode compareMode, const TexComparePrecision &prec,
357                                        const Vec4 &depths0, const Vec4 &depths1, const float cmpReference,
358                                        const float result, const bool isFixedPointDepth)
359 {
360     DE_ASSERT(prec.pcfBits == 0);
361 
362     const CmpResultSet cmp00 =
363         execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth);
364     const CmpResultSet cmp01 =
365         execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth);
366     const CmpResultSet cmp02 =
367         execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth);
368     const CmpResultSet cmp03 =
369         execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth);
370 
371     const CmpResultSet cmp10 =
372         execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth);
373     const CmpResultSet cmp11 =
374         execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth);
375     const CmpResultSet cmp12 =
376         execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth);
377     const CmpResultSet cmp13 =
378         execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth);
379 
380     const bool canBeTrue = cmp00.isTrue || cmp01.isTrue || cmp02.isTrue || cmp03.isTrue || cmp10.isTrue ||
381                            cmp11.isTrue || cmp12.isTrue || cmp13.isTrue;
382     const bool canBeFalse = cmp00.isFalse || cmp01.isFalse || cmp02.isFalse || cmp03.isFalse || cmp10.isFalse ||
383                             cmp11.isFalse || cmp12.isFalse || cmp13.isFalse;
384 
385     const float resErr = computeFixedPointError(prec.resultBits);
386 
387     const float minBound = canBeFalse ? 0.0f : 1.0f;
388     const float maxBound = canBeTrue ? 1.0f : 0.0f;
389 
390     return de::inRange(result, minBound - resErr, maxBound + resErr);
391 }
392 
isTrilinearPCFCompareValid(const Sampler::CompareMode compareMode,const TexComparePrecision & prec,const Vec4 & depths0,const Vec4 & depths1,const Vec2 & xBounds0,const Vec2 & yBounds0,const Vec2 & xBounds1,const Vec2 & yBounds1,const Vec2 & fBounds,const float cmpReference,const float result,const bool isFixedPointDepth)393 static bool isTrilinearPCFCompareValid(const Sampler::CompareMode compareMode, const TexComparePrecision &prec,
394                                        const Vec4 &depths0, const Vec4 &depths1, const Vec2 &xBounds0,
395                                        const Vec2 &yBounds0, const Vec2 &xBounds1, const Vec2 &yBounds1,
396                                        const Vec2 &fBounds, const float cmpReference, const float result,
397                                        const bool isFixedPointDepth)
398 {
399     DE_ASSERT(0.0f <= xBounds0.x() && xBounds0.x() <= xBounds0.y() && xBounds0.y() <= 1.0f);
400     DE_ASSERT(0.0f <= yBounds0.x() && yBounds0.x() <= yBounds0.y() && yBounds0.y() <= 1.0f);
401     DE_ASSERT(0.0f <= xBounds1.x() && xBounds1.x() <= xBounds1.y() && xBounds1.y() <= 1.0f);
402     DE_ASSERT(0.0f <= yBounds1.x() && yBounds1.x() <= yBounds1.y() && yBounds1.y() <= 1.0f);
403     DE_ASSERT(0.0f <= fBounds.x() && fBounds.x() <= fBounds.y() && fBounds.y() <= 1.0f);
404     DE_ASSERT(prec.pcfBits > 0);
405 
406     const CmpResultSet cmp00 =
407         execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth);
408     const CmpResultSet cmp01 =
409         execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth);
410     const CmpResultSet cmp02 =
411         execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth);
412     const CmpResultSet cmp03 =
413         execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth);
414 
415     const CmpResultSet cmp10 =
416         execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth);
417     const CmpResultSet cmp11 =
418         execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth);
419     const CmpResultSet cmp12 =
420         execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth);
421     const CmpResultSet cmp13 =
422         execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth);
423 
424     const uint32_t isTrue = (uint32_t(cmp00.isTrue) << 0) | (uint32_t(cmp01.isTrue) << 1) |
425                             (uint32_t(cmp02.isTrue) << 2) | (uint32_t(cmp03.isTrue) << 3) |
426                             (uint32_t(cmp10.isTrue) << 4) | (uint32_t(cmp11.isTrue) << 5) |
427                             (uint32_t(cmp12.isTrue) << 6) | (uint32_t(cmp13.isTrue) << 7);
428     const uint32_t isFalse = (uint32_t(cmp00.isFalse) << 0) | (uint32_t(cmp01.isFalse) << 1) |
429                              (uint32_t(cmp02.isFalse) << 2) | (uint32_t(cmp03.isFalse) << 3) |
430                              (uint32_t(cmp10.isFalse) << 4) | (uint32_t(cmp11.isFalse) << 5) |
431                              (uint32_t(cmp12.isFalse) << 6) | (uint32_t(cmp13.isFalse) << 7);
432 
433     // Error parameters
434     const float pcfErr   = computeFixedPointError(prec.pcfBits);
435     const float resErr   = computeFixedPointError(prec.resultBits);
436     const float totalErr = pcfErr + resErr;
437 
438     // Iterate over all valid combinations.
439     for (uint32_t comb = 0; comb < (1 << 8); comb++)
440     {
441         // Filter out invalid combinations.
442         if (((comb & isTrue) | (~comb & isFalse)) != (1 << 8) - 1)
443             continue;
444 
445         const BVec4 cmpTrue0 = extractBVec4(comb, 0);
446         const BVec4 cmpTrue1 = extractBVec4(comb, 4);
447         const Vec4 refVal0   = select(Vec4(1.0f), Vec4(0.0f), cmpTrue0);
448         const Vec4 refVal1   = select(Vec4(1.0f), Vec4(0.0f), cmpTrue1);
449 
450         // Bilinear interpolation within levels.
451         const float v00   = bilinearInterpolate(refVal0, xBounds0.x(), yBounds0.x());
452         const float v01   = bilinearInterpolate(refVal0, xBounds0.y(), yBounds0.x());
453         const float v02   = bilinearInterpolate(refVal0, xBounds0.x(), yBounds0.y());
454         const float v03   = bilinearInterpolate(refVal0, xBounds0.y(), yBounds0.y());
455         const float minV0 = de::min(v00, de::min(v01, de::min(v02, v03)));
456         const float maxV0 = de::max(v00, de::max(v01, de::max(v02, v03)));
457 
458         const float v10   = bilinearInterpolate(refVal1, xBounds1.x(), yBounds1.x());
459         const float v11   = bilinearInterpolate(refVal1, xBounds1.y(), yBounds1.x());
460         const float v12   = bilinearInterpolate(refVal1, xBounds1.x(), yBounds1.y());
461         const float v13   = bilinearInterpolate(refVal1, xBounds1.y(), yBounds1.y());
462         const float minV1 = de::min(v10, de::min(v11, de::min(v12, v13)));
463         const float maxV1 = de::max(v10, de::max(v11, de::max(v12, v13)));
464 
465         // Compute min-max bounds by filtering between minimum bounds and maximum bounds between levels.
466         // HW can end up choosing pretty much any of samples between levels, and thus interpolating
467         // between minimums should yield lower bound for range, and same for upper bound.
468         // \todo [2013-07-17 pyry] This seems separable? Can this be optimized? At least ranges could be pre-computed and later combined.
469         const float minF0 = minV0 * (1.0f - fBounds.x()) + minV1 * fBounds.x();
470         const float minF1 = minV0 * (1.0f - fBounds.y()) + minV1 * fBounds.y();
471         const float maxF0 = maxV0 * (1.0f - fBounds.x()) + maxV1 * fBounds.x();
472         const float maxF1 = maxV0 * (1.0f - fBounds.y()) + maxV1 * fBounds.y();
473 
474         const float minF = de::min(minF0, minF1);
475         const float maxF = de::max(maxF0, maxF1);
476 
477         const float minR = minF - totalErr;
478         const float maxR = maxF + totalErr;
479 
480         if (de::inRange(result, minR, maxR))
481             return true;
482     }
483 
484     return false;
485 }
486 
isTrilinearCompareValid(const Sampler::CompareMode compareMode,const TexComparePrecision & prec,const Vec4 & depths0,const Vec4 & depths1,const Vec2 & xBounds0,const Vec2 & yBounds0,const Vec2 & xBounds1,const Vec2 & yBounds1,const Vec2 & fBounds,const float cmpReference,const float result,const bool isFixedPointDepth)487 static bool isTrilinearCompareValid(const Sampler::CompareMode compareMode, const TexComparePrecision &prec,
488                                     const Vec4 &depths0, const Vec4 &depths1, const Vec2 &xBounds0,
489                                     const Vec2 &yBounds0, const Vec2 &xBounds1, const Vec2 &yBounds1,
490                                     const Vec2 &fBounds, const float cmpReference, const float result,
491                                     const bool isFixedPointDepth)
492 {
493     if (prec.pcfBits > 0)
494         return isTrilinearPCFCompareValid(compareMode, prec, depths0, depths1, xBounds0, yBounds0, xBounds1, yBounds1,
495                                           fBounds, cmpReference, result, isFixedPointDepth);
496     else
497         return isTrilinearAnyCompareValid(compareMode, prec, depths0, depths1, cmpReference, result, isFixedPointDepth);
498 }
499 
isNearestCompareResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const TexComparePrecision & prec,const Vec2 & coord,const int coordZ,const float cmpReference,const float result)500 static bool isNearestCompareResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
501                                         const TexComparePrecision &prec, const Vec2 &coord, const int coordZ,
502                                         const float cmpReference, const float result)
503 {
504     const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level.getFormat());
505     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(),
506                                                          prec.coordBits.x(), prec.uvwBits.x());
507     const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(),
508                                                          prec.coordBits.y(), prec.uvwBits.y());
509 
510     // Integer coordinates - without wrap mode
511     const int minI = deFloorFloatToInt32(uBounds.x());
512     const int maxI = deFloorFloatToInt32(uBounds.y());
513     const int minJ = deFloorFloatToInt32(vBounds.x());
514     const int maxJ = deFloorFloatToInt32(vBounds.y());
515 
516     for (int j = minJ; j <= maxJ; j++)
517     {
518         for (int i = minI; i <= maxI; i++)
519         {
520             const int x       = wrap(sampler.wrapS, i, level.getWidth());
521             const int y       = wrap(sampler.wrapT, j, level.getHeight());
522             const float depth = lookupDepth(level, sampler, x, y, coordZ);
523             const CmpResultSet resSet =
524                 execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth);
525 
526             if (isResultInSet(resSet, result, prec.resultBits))
527                 return true;
528         }
529     }
530 
531     return false;
532 }
533 
isLinearCompareResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const TexComparePrecision & prec,const Vec2 & coord,const int coordZ,const float cmpReference,const float result)534 static bool isLinearCompareResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
535                                        const TexComparePrecision &prec, const Vec2 &coord, const int coordZ,
536                                        const float cmpReference, const float result)
537 {
538     const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level.getFormat());
539     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(),
540                                                          prec.coordBits.x(), prec.uvwBits.x());
541     const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(),
542                                                          prec.coordBits.y(), prec.uvwBits.y());
543 
544     // Integer coordinate bounds for (x0,y0) - without wrap mode
545     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
546     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
547     const int minJ = deFloorFloatToInt32(vBounds.x() - 0.5f);
548     const int maxJ = deFloorFloatToInt32(vBounds.y() - 0.5f);
549 
550     const int w = level.getWidth();
551     const int h = level.getHeight();
552 
553     // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
554 
555     for (int j = minJ; j <= maxJ; j++)
556     {
557         for (int i = minI; i <= maxI; i++)
558         {
559             // Wrapped coordinates
560             const int x0 = wrap(sampler.wrapS, i, w);
561             const int x1 = wrap(sampler.wrapS, i + 1, w);
562             const int y0 = wrap(sampler.wrapT, j, h);
563             const int y1 = wrap(sampler.wrapT, j + 1, h);
564 
565             // Bounds for filtering factors
566             const float minA = de::clamp((uBounds.x() - 0.5f) - float(i), 0.0f, 1.0f);
567             const float maxA = de::clamp((uBounds.y() - 0.5f) - float(i), 0.0f, 1.0f);
568             const float minB = de::clamp((vBounds.x() - 0.5f) - float(j), 0.0f, 1.0f);
569             const float maxB = de::clamp((vBounds.y() - 0.5f) - float(j), 0.0f, 1.0f);
570 
571             const Vec4 depths(lookupDepth(level, sampler, x0, y0, coordZ), lookupDepth(level, sampler, x1, y0, coordZ),
572                               lookupDepth(level, sampler, x0, y1, coordZ), lookupDepth(level, sampler, x1, y1, coordZ));
573 
574             if (isBilinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), Vec2(minB, maxB), cmpReference,
575                                        result, isFixedPointDepth))
576                 return true;
577         }
578     }
579 
580     return false;
581 }
582 
isLevelCompareResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const TexComparePrecision & prec,const Vec2 & coord,const int coordZ,const float cmpReference,const float result)583 static bool isLevelCompareResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
584                                       const Sampler::FilterMode filterMode, const TexComparePrecision &prec,
585                                       const Vec2 &coord, const int coordZ, const float cmpReference, const float result)
586 {
587     if (filterMode == Sampler::LINEAR)
588         return isLinearCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result);
589     else
590         return isNearestCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result);
591 }
592 
isNearestMipmapLinearCompareResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const TexComparePrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const float cmpReference,const float result)593 static bool isNearestMipmapLinearCompareResultValid(const ConstPixelBufferAccess &level0,
594                                                     const ConstPixelBufferAccess &level1, const Sampler &sampler,
595                                                     const TexComparePrecision &prec, const Vec2 &coord,
596                                                     const int coordZ, const Vec2 &fBounds, const float cmpReference,
597                                                     const float result)
598 {
599     const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level0.getFormat());
600 
601     const int w0 = level0.getWidth();
602     const int w1 = level1.getWidth();
603     const int h0 = level0.getHeight();
604     const int h1 = level1.getHeight();
605 
606     const Vec2 uBounds0 =
607         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
608     const Vec2 uBounds1 =
609         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
610     const Vec2 vBounds0 =
611         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
612     const Vec2 vBounds1 =
613         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
614 
615     // Integer coordinates - without wrap mode
616     const int minI0 = deFloorFloatToInt32(uBounds0.x());
617     const int maxI0 = deFloorFloatToInt32(uBounds0.y());
618     const int minI1 = deFloorFloatToInt32(uBounds1.x());
619     const int maxI1 = deFloorFloatToInt32(uBounds1.y());
620     const int minJ0 = deFloorFloatToInt32(vBounds0.x());
621     const int maxJ0 = deFloorFloatToInt32(vBounds0.y());
622     const int minJ1 = deFloorFloatToInt32(vBounds1.x());
623     const int maxJ1 = deFloorFloatToInt32(vBounds1.y());
624 
625     for (int j0 = minJ0; j0 <= maxJ0; j0++)
626     {
627         for (int i0 = minI0; i0 <= maxI0; i0++)
628         {
629             const float depth0 =
630                 lookupDepth(level0, sampler, wrap(sampler.wrapS, i0, w0), wrap(sampler.wrapT, j0, h0), coordZ);
631 
632             for (int j1 = minJ1; j1 <= maxJ1; j1++)
633             {
634                 for (int i1 = minI1; i1 <= maxI1; i1++)
635                 {
636                     const float depth1 =
637                         lookupDepth(level1, sampler, wrap(sampler.wrapS, i1, w1), wrap(sampler.wrapT, j1, h1), coordZ);
638 
639                     if (isLinearCompareValid(sampler.compare, prec, Vec2(depth0, depth1), fBounds, cmpReference, result,
640                                              isFixedPointDepth))
641                         return true;
642                 }
643             }
644         }
645     }
646 
647     return false;
648 }
649 
isLinearMipmapLinearCompareResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const TexComparePrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const float cmpReference,const float result)650 static bool isLinearMipmapLinearCompareResultValid(const ConstPixelBufferAccess &level0,
651                                                    const ConstPixelBufferAccess &level1, const Sampler &sampler,
652                                                    const TexComparePrecision &prec, const Vec2 &coord, const int coordZ,
653                                                    const Vec2 &fBounds, const float cmpReference, const float result)
654 {
655     const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level0.getFormat());
656 
657     // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
658     //                           Right now this allows pairing any two valid bilinear quads.
659 
660     const int w0 = level0.getWidth();
661     const int w1 = level1.getWidth();
662     const int h0 = level0.getHeight();
663     const int h1 = level1.getHeight();
664 
665     const Vec2 uBounds0 =
666         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
667     const Vec2 uBounds1 =
668         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
669     const Vec2 vBounds0 =
670         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
671     const Vec2 vBounds1 =
672         computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord.y(), prec.coordBits.y(), prec.uvwBits.y());
673 
674     // Integer coordinates - without wrap mode
675     const int minI0 = deFloorFloatToInt32(uBounds0.x() - 0.5f);
676     const int maxI0 = deFloorFloatToInt32(uBounds0.y() - 0.5f);
677     const int minI1 = deFloorFloatToInt32(uBounds1.x() - 0.5f);
678     const int maxI1 = deFloorFloatToInt32(uBounds1.y() - 0.5f);
679     const int minJ0 = deFloorFloatToInt32(vBounds0.x() - 0.5f);
680     const int maxJ0 = deFloorFloatToInt32(vBounds0.y() - 0.5f);
681     const int minJ1 = deFloorFloatToInt32(vBounds1.x() - 0.5f);
682     const int maxJ1 = deFloorFloatToInt32(vBounds1.y() - 0.5f);
683 
684     for (int j0 = minJ0; j0 <= maxJ0; j0++)
685     {
686         for (int i0 = minI0; i0 <= maxI0; i0++)
687         {
688             const float minA0 = de::clamp((uBounds0.x() - 0.5f) - float(i0), 0.0f, 1.0f);
689             const float maxA0 = de::clamp((uBounds0.y() - 0.5f) - float(i0), 0.0f, 1.0f);
690             const float minB0 = de::clamp((vBounds0.x() - 0.5f) - float(j0), 0.0f, 1.0f);
691             const float maxB0 = de::clamp((vBounds0.y() - 0.5f) - float(j0), 0.0f, 1.0f);
692             Vec4 depths0;
693 
694             {
695                 const int x0 = wrap(sampler.wrapS, i0, w0);
696                 const int x1 = wrap(sampler.wrapS, i0 + 1, w0);
697                 const int y0 = wrap(sampler.wrapT, j0, h0);
698                 const int y1 = wrap(sampler.wrapT, j0 + 1, h0);
699 
700                 depths0[0] = lookupDepth(level0, sampler, x0, y0, coordZ);
701                 depths0[1] = lookupDepth(level0, sampler, x1, y0, coordZ);
702                 depths0[2] = lookupDepth(level0, sampler, x0, y1, coordZ);
703                 depths0[3] = lookupDepth(level0, sampler, x1, y1, coordZ);
704             }
705 
706             for (int j1 = minJ1; j1 <= maxJ1; j1++)
707             {
708                 for (int i1 = minI1; i1 <= maxI1; i1++)
709                 {
710                     const float minA1 = de::clamp((uBounds1.x() - 0.5f) - float(i1), 0.0f, 1.0f);
711                     const float maxA1 = de::clamp((uBounds1.y() - 0.5f) - float(i1), 0.0f, 1.0f);
712                     const float minB1 = de::clamp((vBounds1.x() - 0.5f) - float(j1), 0.0f, 1.0f);
713                     const float maxB1 = de::clamp((vBounds1.y() - 0.5f) - float(j1), 0.0f, 1.0f);
714                     Vec4 depths1;
715 
716                     {
717                         const int x0 = wrap(sampler.wrapS, i1, w1);
718                         const int x1 = wrap(sampler.wrapS, i1 + 1, w1);
719                         const int y0 = wrap(sampler.wrapT, j1, h1);
720                         const int y1 = wrap(sampler.wrapT, j1 + 1, h1);
721 
722                         depths1[0] = lookupDepth(level1, sampler, x0, y0, coordZ);
723                         depths1[1] = lookupDepth(level1, sampler, x1, y0, coordZ);
724                         depths1[2] = lookupDepth(level1, sampler, x0, y1, coordZ);
725                         depths1[3] = lookupDepth(level1, sampler, x1, y1, coordZ);
726                     }
727 
728                     if (isTrilinearCompareValid(sampler.compare, prec, depths0, depths1, Vec2(minA0, maxA0),
729                                                 Vec2(minB0, maxB0), Vec2(minA1, maxA1), Vec2(minB1, maxB1), fBounds,
730                                                 cmpReference, result, isFixedPointDepth))
731                         return true;
732                 }
733             }
734         }
735     }
736 
737     return false;
738 }
739 
isMipmapLinearCompareResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const TexComparePrecision & prec,const Vec2 & coord,const int coordZ,const Vec2 & fBounds,const float cmpReference,const float result)740 static bool isMipmapLinearCompareResultValid(const ConstPixelBufferAccess &level0, const ConstPixelBufferAccess &level1,
741                                              const Sampler &sampler, const Sampler::FilterMode levelFilter,
742                                              const TexComparePrecision &prec, const Vec2 &coord, const int coordZ,
743                                              const Vec2 &fBounds, const float cmpReference, const float result)
744 {
745     if (levelFilter == Sampler::LINEAR)
746         return isLinearMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds,
747                                                       cmpReference, result);
748     else
749         return isNearestMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds,
750                                                        cmpReference, result);
751 }
752 
isTexCompareResultValid(const Texture2DView & texture,const Sampler & sampler,const TexComparePrecision & prec,const Vec2 & coord,const Vec2 & lodBounds,const float cmpReference,const float result)753 bool isTexCompareResultValid(const Texture2DView &texture, const Sampler &sampler, const TexComparePrecision &prec,
754                              const Vec2 &coord, const Vec2 &lodBounds, const float cmpReference, const float result)
755 {
756     const float minLod        = lodBounds.x();
757     const float maxLod        = lodBounds.y();
758     const bool canBeMagnified = minLod <= sampler.lodThreshold;
759     const bool canBeMinified  = maxLod > sampler.lodThreshold;
760 
761     DE_ASSERT(isSamplerSupported(sampler));
762 
763     if (canBeMagnified)
764     {
765         if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, cmpReference,
766                                       result))
767             return true;
768     }
769 
770     if (canBeMinified)
771     {
772         const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
773         const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
774         const int minTexLevel      = 0;
775         const int maxTexLevel      = texture.getNumLevels() - 1;
776 
777         DE_ASSERT(minTexLevel < maxTexLevel);
778 
779         if (isLinearMipmap)
780         {
781             const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
782             const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
783 
784             DE_ASSERT(minLevel <= maxLevel);
785 
786             for (int level = minLevel; level <= maxLevel; level++)
787             {
788                 const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
789                 const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
790 
791                 if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler,
792                                                      getLevelFilter(sampler.minFilter), prec, coord, 0,
793                                                      Vec2(minF, maxF), cmpReference, result))
794                     return true;
795             }
796         }
797         else if (isNearestMipmap)
798         {
799             // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
800             //         decision to allow floor(lod + 0.5) as well.
801             const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
802             const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
803 
804             DE_ASSERT(minLevel <= maxLevel);
805 
806             for (int level = minLevel; level <= maxLevel; level++)
807             {
808                 if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec,
809                                               coord, 0, cmpReference, result))
810                     return true;
811             }
812         }
813         else
814         {
815             if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, cmpReference,
816                                           result))
817                 return true;
818         }
819     }
820 
821     return false;
822 }
823 
isSeamplessLinearMipmapLinearCompareResultValid(const TextureCubeView & texture,const int baseLevelNdx,const Sampler & sampler,const TexComparePrecision & prec,const CubeFaceFloatCoords & coords,const Vec2 & fBounds,const float cmpReference,const float result)824 static bool isSeamplessLinearMipmapLinearCompareResultValid(const TextureCubeView &texture, const int baseLevelNdx,
825                                                             const Sampler &sampler, const TexComparePrecision &prec,
826                                                             const CubeFaceFloatCoords &coords, const Vec2 &fBounds,
827                                                             const float cmpReference, const float result)
828 {
829     const bool isFixedPointDepth =
830         isFixedPointDepthTextureFormat(texture.getLevelFace(baseLevelNdx, CUBEFACE_NEGATIVE_X).getFormat());
831     const int size0 = texture.getLevelFace(baseLevelNdx, coords.face).getWidth();
832     const int size1 = texture.getLevelFace(baseLevelNdx + 1, coords.face).getWidth();
833 
834     const Vec2 uBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.s, prec.coordBits.x(),
835                                                           prec.uvwBits.x());
836     const Vec2 uBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.s, prec.coordBits.x(),
837                                                           prec.uvwBits.x());
838     const Vec2 vBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.t, prec.coordBits.y(),
839                                                           prec.uvwBits.y());
840     const Vec2 vBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.t, prec.coordBits.y(),
841                                                           prec.uvwBits.y());
842 
843     // Integer coordinates - without wrap mode
844     const int minI0 = deFloorFloatToInt32(uBounds0.x() - 0.5f);
845     const int maxI0 = deFloorFloatToInt32(uBounds0.y() - 0.5f);
846     const int minI1 = deFloorFloatToInt32(uBounds1.x() - 0.5f);
847     const int maxI1 = deFloorFloatToInt32(uBounds1.y() - 0.5f);
848     const int minJ0 = deFloorFloatToInt32(vBounds0.x() - 0.5f);
849     const int maxJ0 = deFloorFloatToInt32(vBounds0.y() - 0.5f);
850     const int minJ1 = deFloorFloatToInt32(vBounds1.x() - 0.5f);
851     const int maxJ1 = deFloorFloatToInt32(vBounds1.y() - 0.5f);
852 
853     tcu::ConstPixelBufferAccess faces0[CUBEFACE_LAST];
854     tcu::ConstPixelBufferAccess faces1[CUBEFACE_LAST];
855 
856     for (int face = 0; face < CUBEFACE_LAST; face++)
857     {
858         faces0[face] = texture.getLevelFace(baseLevelNdx, CubeFace(face));
859         faces1[face] = texture.getLevelFace(baseLevelNdx + 1, CubeFace(face));
860     }
861 
862     for (int j0 = minJ0; j0 <= maxJ0; j0++)
863     {
864         for (int i0 = minI0; i0 <= maxI0; i0++)
865         {
866             const float minA0 = de::clamp((uBounds0.x() - 0.5f) - float(i0), 0.0f, 1.0f);
867             const float maxA0 = de::clamp((uBounds0.y() - 0.5f) - float(i0), 0.0f, 1.0f);
868             const float minB0 = de::clamp((vBounds0.x() - 0.5f) - float(j0), 0.0f, 1.0f);
869             const float maxB0 = de::clamp((vBounds0.y() - 0.5f) - float(j0), 0.0f, 1.0f);
870             Vec4 depths0;
871 
872             {
873                 const CubeFaceIntCoords c00 =
874                     remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0 + 0, j0 + 0)), size0);
875                 const CubeFaceIntCoords c10 =
876                     remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0 + 1, j0 + 0)), size0);
877                 const CubeFaceIntCoords c01 =
878                     remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0 + 0, j0 + 1)), size0);
879                 const CubeFaceIntCoords c11 =
880                     remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0 + 1, j0 + 1)), size0);
881 
882                 // If any of samples is out of both edges, implementations can do pretty much anything according to spec.
883                 // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
884                 if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST ||
885                     c11.face == CUBEFACE_LAST)
886                     return true;
887 
888                 depths0[0] = lookupDepthNoBorder(faces0[c00.face], sampler, c00.s, c00.t);
889                 depths0[1] = lookupDepthNoBorder(faces0[c10.face], sampler, c10.s, c10.t);
890                 depths0[2] = lookupDepthNoBorder(faces0[c01.face], sampler, c01.s, c01.t);
891                 depths0[3] = lookupDepthNoBorder(faces0[c11.face], sampler, c11.s, c11.t);
892             }
893 
894             for (int j1 = minJ1; j1 <= maxJ1; j1++)
895             {
896                 for (int i1 = minI1; i1 <= maxI1; i1++)
897                 {
898                     const float minA1 = de::clamp((uBounds1.x() - 0.5f) - float(i1), 0.0f, 1.0f);
899                     const float maxA1 = de::clamp((uBounds1.y() - 0.5f) - float(i1), 0.0f, 1.0f);
900                     const float minB1 = de::clamp((vBounds1.x() - 0.5f) - float(j1), 0.0f, 1.0f);
901                     const float maxB1 = de::clamp((vBounds1.y() - 0.5f) - float(j1), 0.0f, 1.0f);
902                     Vec4 depths1;
903 
904                     {
905                         const CubeFaceIntCoords c00 =
906                             remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1 + 0, j1 + 0)), size1);
907                         const CubeFaceIntCoords c10 =
908                             remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1 + 1, j1 + 0)), size1);
909                         const CubeFaceIntCoords c01 =
910                             remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1 + 0, j1 + 1)), size1);
911                         const CubeFaceIntCoords c11 =
912                             remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1 + 1, j1 + 1)), size1);
913 
914                         if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST ||
915                             c11.face == CUBEFACE_LAST)
916                             return true;
917 
918                         depths1[0] = lookupDepthNoBorder(faces1[c00.face], sampler, c00.s, c00.t);
919                         depths1[1] = lookupDepthNoBorder(faces1[c10.face], sampler, c10.s, c10.t);
920                         depths1[2] = lookupDepthNoBorder(faces1[c01.face], sampler, c01.s, c01.t);
921                         depths1[3] = lookupDepthNoBorder(faces1[c11.face], sampler, c11.s, c11.t);
922                     }
923 
924                     if (isTrilinearCompareValid(sampler.compare, prec, depths0, depths1, Vec2(minA0, maxA0),
925                                                 Vec2(minB0, maxB0), Vec2(minA1, maxA1), Vec2(minB1, maxB1), fBounds,
926                                                 cmpReference, result, isFixedPointDepth))
927                         return true;
928                 }
929             }
930         }
931     }
932 
933     return false;
934 }
935 
isCubeMipmapLinearCompareResultValid(const TextureCubeView & texture,const int baseLevelNdx,const Sampler & sampler,const Sampler::FilterMode levelFilter,const TexComparePrecision & prec,const CubeFaceFloatCoords & coords,const Vec2 & fBounds,const float cmpReference,const float result)936 static bool isCubeMipmapLinearCompareResultValid(const TextureCubeView &texture, const int baseLevelNdx,
937                                                  const Sampler &sampler, const Sampler::FilterMode levelFilter,
938                                                  const TexComparePrecision &prec, const CubeFaceFloatCoords &coords,
939                                                  const Vec2 &fBounds, const float cmpReference, const float result)
940 {
941     if (levelFilter == Sampler::LINEAR)
942     {
943         if (sampler.seamlessCubeMap)
944             return isSeamplessLinearMipmapLinearCompareResultValid(texture, baseLevelNdx, sampler, prec, coords,
945                                                                    fBounds, cmpReference, result);
946         else
947             return isLinearMipmapLinearCompareResultValid(
948                 texture.getLevelFace(baseLevelNdx, coords.face), texture.getLevelFace(baseLevelNdx + 1, coords.face),
949                 sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, cmpReference, result);
950     }
951     else
952         return isNearestMipmapLinearCompareResultValid(
953             texture.getLevelFace(baseLevelNdx, coords.face), texture.getLevelFace(baseLevelNdx + 1, coords.face),
954             sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, cmpReference, result);
955 }
956 
isSeamlessLinearCompareResultValid(const TextureCubeView & texture,const int levelNdx,const Sampler & sampler,const TexComparePrecision & prec,const CubeFaceFloatCoords & coords,const float cmpReference,const float result)957 static bool isSeamlessLinearCompareResultValid(const TextureCubeView &texture, const int levelNdx,
958                                                const Sampler &sampler, const TexComparePrecision &prec,
959                                                const CubeFaceFloatCoords &coords, const float cmpReference,
960                                                const float result)
961 {
962     const bool isFixedPointDepth =
963         isFixedPointDepthTextureFormat(texture.getLevelFace(levelNdx, CUBEFACE_NEGATIVE_X).getFormat());
964     const int size = texture.getLevelFace(levelNdx, coords.face).getWidth();
965 
966     const Vec2 uBounds =
967         computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
968     const Vec2 vBounds =
969         computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
970 
971     // Integer coordinate bounds for (x0,y0) - without wrap mode
972     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
973     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
974     const int minJ = deFloorFloatToInt32(vBounds.x() - 0.5f);
975     const int maxJ = deFloorFloatToInt32(vBounds.y() - 0.5f);
976 
977     // Face accesses
978     ConstPixelBufferAccess faces[CUBEFACE_LAST];
979     for (int face = 0; face < CUBEFACE_LAST; face++)
980         faces[face] = texture.getLevelFace(levelNdx, CubeFace(face));
981 
982     for (int j = minJ; j <= maxJ; j++)
983     {
984         for (int i = minI; i <= maxI; i++)
985         {
986             const CubeFaceIntCoords c00 =
987                 remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i + 0, j + 0)), size);
988             const CubeFaceIntCoords c10 =
989                 remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i + 1, j + 0)), size);
990             const CubeFaceIntCoords c01 =
991                 remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i + 0, j + 1)), size);
992             const CubeFaceIntCoords c11 =
993                 remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i + 1, j + 1)), size);
994 
995             // If any of samples is out of both edges, implementations can do pretty much anything according to spec.
996             // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color.
997             if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST ||
998                 c11.face == CUBEFACE_LAST)
999                 return true;
1000 
1001             // Bounds for filtering factors
1002             const float minA = de::clamp((uBounds.x() - 0.5f) - float(i), 0.0f, 1.0f);
1003             const float maxA = de::clamp((uBounds.y() - 0.5f) - float(i), 0.0f, 1.0f);
1004             const float minB = de::clamp((vBounds.x() - 0.5f) - float(j), 0.0f, 1.0f);
1005             const float maxB = de::clamp((vBounds.y() - 0.5f) - float(j), 0.0f, 1.0f);
1006 
1007             Vec4 depths;
1008             depths[0] = lookupDepthNoBorder(faces[c00.face], sampler, c00.s, c00.t);
1009             depths[1] = lookupDepthNoBorder(faces[c10.face], sampler, c10.s, c10.t);
1010             depths[2] = lookupDepthNoBorder(faces[c01.face], sampler, c01.s, c01.t);
1011             depths[3] = lookupDepthNoBorder(faces[c11.face], sampler, c11.s, c11.t);
1012 
1013             if (isBilinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), Vec2(minB, maxB), cmpReference,
1014                                        result, isFixedPointDepth))
1015                 return true;
1016         }
1017     }
1018 
1019     return false;
1020 }
1021 
isCubeLevelCompareResultValid(const TextureCubeView & texture,const int levelNdx,const Sampler & sampler,const Sampler::FilterMode filterMode,const TexComparePrecision & prec,const CubeFaceFloatCoords & coords,const float cmpReference,const float result)1022 static bool isCubeLevelCompareResultValid(const TextureCubeView &texture, const int levelNdx, const Sampler &sampler,
1023                                           const Sampler::FilterMode filterMode, const TexComparePrecision &prec,
1024                                           const CubeFaceFloatCoords &coords, const float cmpReference,
1025                                           const float result)
1026 {
1027     if (filterMode == Sampler::LINEAR)
1028     {
1029         if (sampler.seamlessCubeMap)
1030             return isSeamlessLinearCompareResultValid(texture, levelNdx, sampler, prec, coords, cmpReference, result);
1031         else
1032             return isLinearCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec,
1033                                               Vec2(coords.s, coords.t), 0, cmpReference, result);
1034     }
1035     else
1036         return isNearestCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec,
1037                                            Vec2(coords.s, coords.t), 0, cmpReference, result);
1038 }
1039 
isCubeLevelCompareResultValid(const TextureCubeArrayView & texture,const int baseLevelNdx,const Sampler & sampler,const Sampler::FilterMode filterMode,const TexComparePrecision & prec,const CubeFaceFloatCoords & coords,const float depth,const float cmpReference,const float result)1040 static bool isCubeLevelCompareResultValid(const TextureCubeArrayView &texture, const int baseLevelNdx,
1041                                           const Sampler &sampler, const Sampler::FilterMode filterMode,
1042                                           const TexComparePrecision &prec, const CubeFaceFloatCoords &coords,
1043                                           const float depth, const float cmpReference, const float result)
1044 {
1045     const float depthErr =
1046         computeFloatingPointError(depth, prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z());
1047     const float minZ    = depth - depthErr;
1048     const float maxZ    = depth + depthErr;
1049     const int minLayer  = de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers() - 1);
1050     const int maxLayer  = de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers() - 1);
1051     const int numLevels = texture.getNumLevels();
1052 
1053     for (int layer = minLayer; layer <= maxLayer; layer++)
1054     {
1055         std::vector<tcu::ConstPixelBufferAccess> levelsAtLayer[CUBEFACE_LAST];
1056 
1057         for (int faceNdx = 0; faceNdx < CUBEFACE_LAST; faceNdx++)
1058         {
1059             levelsAtLayer[faceNdx].resize(numLevels);
1060 
1061             for (int levelNdx = 0; levelNdx < numLevels; ++levelNdx)
1062             {
1063                 const tcu::ConstPixelBufferAccess &level = texture.getLevel(levelNdx);
1064 
1065                 levelsAtLayer[faceNdx][levelNdx] =
1066                     ConstPixelBufferAccess(level.getFormat(), level.getWidth(), level.getHeight(), 1,
1067                                            level.getPixelPtr(0, 0, CUBEFACE_LAST * layer + faceNdx));
1068             }
1069         }
1070 
1071         const tcu::ConstPixelBufferAccess *levels[CUBEFACE_LAST]{
1072             // Such a strange order due to sampleCompare TextureCubeArrayView uses getCubeArrayFaceIndex while in TextureCubeView does not
1073             &levelsAtLayer[1][0], &levelsAtLayer[0][0], &levelsAtLayer[3][0],
1074             &levelsAtLayer[2][0], &levelsAtLayer[5][0], &levelsAtLayer[4][0],
1075         };
1076 
1077         if (isCubeLevelCompareResultValid(TextureCubeView(numLevels, levels), baseLevelNdx, sampler, filterMode, prec,
1078                                           coords, cmpReference, result))
1079             return true;
1080     }
1081 
1082     return false;
1083 }
1084 
isCubeMipmapLinearCompareResultValid(const TextureCubeArrayView & texture,const int baseLevelNdx,const Sampler & sampler,const Sampler::FilterMode levelFilter,const TexComparePrecision & prec,const CubeFaceFloatCoords & coords,const float depth,const Vec2 & fBounds,const float cmpReference,const float result)1085 static bool isCubeMipmapLinearCompareResultValid(const TextureCubeArrayView &texture, const int baseLevelNdx,
1086                                                  const Sampler &sampler, const Sampler::FilterMode levelFilter,
1087                                                  const TexComparePrecision &prec, const CubeFaceFloatCoords &coords,
1088                                                  const float depth, const Vec2 &fBounds, const float cmpReference,
1089                                                  const float result)
1090 {
1091     const float depthErr =
1092         computeFloatingPointError(depth, prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z());
1093     const float minZ    = depth - depthErr;
1094     const float maxZ    = depth + depthErr;
1095     const int minLayer  = de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers() - 1);
1096     const int maxLayer  = de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers() - 1);
1097     const int numLevels = texture.getNumLevels();
1098 
1099     for (int layer = minLayer; layer <= maxLayer; layer++)
1100     {
1101         std::vector<tcu::ConstPixelBufferAccess> levelsAtLayer[CUBEFACE_LAST];
1102 
1103         for (int faceNdx = 0; faceNdx < CUBEFACE_LAST; faceNdx++)
1104         {
1105             levelsAtLayer[faceNdx].resize(numLevels);
1106 
1107             for (int levelNdx = 0; levelNdx < numLevels; ++levelNdx)
1108             {
1109                 const tcu::ConstPixelBufferAccess &level = texture.getLevel(levelNdx);
1110 
1111                 levelsAtLayer[faceNdx][levelNdx] =
1112                     ConstPixelBufferAccess(level.getFormat(), level.getWidth(), level.getHeight(), 1,
1113                                            level.getPixelPtr(0, 0, CUBEFACE_LAST * layer + faceNdx));
1114             }
1115         }
1116 
1117         const tcu::ConstPixelBufferAccess *levels[CUBEFACE_LAST]{
1118             // Such a strange order due to sampleCompare TextureCubeArrayView uses getCubeArrayFaceIndex while in TextureCubeView does not
1119             &levelsAtLayer[1][0], &levelsAtLayer[0][0], &levelsAtLayer[3][0],
1120             &levelsAtLayer[2][0], &levelsAtLayer[5][0], &levelsAtLayer[4][0],
1121         };
1122 
1123         if (isCubeMipmapLinearCompareResultValid(TextureCubeView(numLevels, levels), baseLevelNdx, sampler, levelFilter,
1124                                                  prec, coords, fBounds, cmpReference, result))
1125             return true;
1126     }
1127 
1128     return false;
1129 }
1130 
isNearestCompareResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const TexComparePrecision & prec,const Vec1 & coord,const int coordZ,const float cmpReference,const float result)1131 static bool isNearestCompareResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
1132                                         const TexComparePrecision &prec, const Vec1 &coord, const int coordZ,
1133                                         const float cmpReference, const float result)
1134 {
1135     const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level.getFormat());
1136     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(),
1137                                                          prec.coordBits.x(), prec.uvwBits.x());
1138 
1139     // Integer coordinates - without wrap mode
1140     const int minI = deFloorFloatToInt32(uBounds.x());
1141     const int maxI = deFloorFloatToInt32(uBounds.y());
1142 
1143     for (int i = minI; i <= maxI; i++)
1144     {
1145         const int x       = wrap(sampler.wrapS, i, level.getWidth());
1146         const float depth = lookupDepth(level, sampler, x, coordZ, 0);
1147         const CmpResultSet resSet =
1148             execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth);
1149 
1150         if (isResultInSet(resSet, result, prec.resultBits))
1151             return true;
1152     }
1153 
1154     return false;
1155 }
1156 
isLinearCompareResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const TexComparePrecision & prec,const Vec1 & coord,const int coordZ,const float cmpReference,const float result)1157 static bool isLinearCompareResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
1158                                        const TexComparePrecision &prec, const Vec1 &coord, const int coordZ,
1159                                        const float cmpReference, const float result)
1160 {
1161     const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level.getFormat());
1162     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(),
1163                                                          prec.coordBits.x(), prec.uvwBits.x());
1164 
1165     // Integer coordinate bounds for (x0,y0) - without wrap mode
1166     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
1167     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
1168 
1169     const int w = level.getWidth();
1170 
1171     // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode.
1172 
1173     for (int i = minI; i <= maxI; i++)
1174     {
1175         // Wrapped coordinates
1176         const int x0 = wrap(sampler.wrapS, i, w);
1177         const int x1 = wrap(sampler.wrapS, i + 1, w);
1178 
1179         // Bounds for filtering factors
1180         const float minA = de::clamp((uBounds.x() - 0.5f) - float(i), 0.0f, 1.0f);
1181         const float maxA = de::clamp((uBounds.y() - 0.5f) - float(i), 0.0f, 1.0f);
1182 
1183         const Vec2 depths(lookupDepth(level, sampler, x0, coordZ, 0), lookupDepth(level, sampler, x1, coordZ, 0));
1184 
1185         if (isLinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), cmpReference, result,
1186                                  isFixedPointDepth))
1187             return true;
1188     }
1189 
1190     return false;
1191 }
1192 
isLevelCompareResultValid(const ConstPixelBufferAccess & level,const Sampler & sampler,const Sampler::FilterMode filterMode,const TexComparePrecision & prec,const Vec1 & coord,const int coordZ,const float cmpReference,const float result)1193 static bool isLevelCompareResultValid(const ConstPixelBufferAccess &level, const Sampler &sampler,
1194                                       const Sampler::FilterMode filterMode, const TexComparePrecision &prec,
1195                                       const Vec1 &coord, const int coordZ, const float cmpReference, const float result)
1196 {
1197     if (filterMode == Sampler::LINEAR)
1198         return isLinearCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result);
1199     else
1200         return isNearestCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result);
1201 }
1202 
isNearestMipmapLinearCompareResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const TexComparePrecision & prec,const Vec1 & coord,const int coordZ,const Vec2 & fBounds,const float cmpReference,const float result)1203 static bool isNearestMipmapLinearCompareResultValid(const ConstPixelBufferAccess &level0,
1204                                                     const ConstPixelBufferAccess &level1, const Sampler &sampler,
1205                                                     const TexComparePrecision &prec, const Vec1 &coord,
1206                                                     const int coordZ, const Vec2 &fBounds, const float cmpReference,
1207                                                     const float result)
1208 {
1209     DE_UNREF(fBounds);
1210     const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level0.getFormat());
1211 
1212     const int w0 = level0.getWidth();
1213     const int w1 = level1.getWidth();
1214 
1215     const Vec2 uBounds0 =
1216         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
1217     const Vec2 uBounds1 =
1218         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
1219 
1220     // Integer coordinates - without wrap mode
1221     const int minI0 = deFloorFloatToInt32(uBounds0.x());
1222     const int maxI0 = deFloorFloatToInt32(uBounds0.y());
1223     const int minI1 = deFloorFloatToInt32(uBounds1.x());
1224     const int maxI1 = deFloorFloatToInt32(uBounds1.y());
1225 
1226     for (int i0 = minI0; i0 <= maxI0; i0++)
1227     {
1228         const float depth0 = lookupDepth(level0, sampler, wrap(sampler.wrapS, i0, w0), coordZ, 0);
1229 
1230         for (int i1 = minI1; i1 <= maxI1; i1++)
1231         {
1232             const float depth1 = lookupDepth(level1, sampler, wrap(sampler.wrapS, i1, w1), coordZ, 0);
1233 
1234             if (isLinearCompareValid(sampler.compare, prec, Vec2(depth0, depth1), fBounds, cmpReference, result,
1235                                      isFixedPointDepth))
1236                 return true;
1237         }
1238     }
1239 
1240     return false;
1241 }
1242 
isLinearMipmapLinearCompareResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const TexComparePrecision & prec,const Vec1 & coord,const int coordZ,const Vec2 & fBounds,const float cmpReference,const float result)1243 static bool isLinearMipmapLinearCompareResultValid(const ConstPixelBufferAccess &level0,
1244                                                    const ConstPixelBufferAccess &level1, const Sampler &sampler,
1245                                                    const TexComparePrecision &prec, const Vec1 &coord, const int coordZ,
1246                                                    const Vec2 &fBounds, const float cmpReference, const float result)
1247 {
1248     DE_UNREF(fBounds);
1249     const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level0.getFormat());
1250 
1251     // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent.
1252     //                           Right now this allows pairing any two valid bilinear quads.
1253 
1254     const int w0 = level0.getWidth();
1255     const int w1 = level1.getWidth();
1256 
1257     const Vec2 uBounds0 =
1258         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
1259     const Vec2 uBounds1 =
1260         computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x());
1261 
1262     // Integer coordinates - without wrap mode
1263     const int minI0 = deFloorFloatToInt32(uBounds0.x() - 0.5f);
1264     const int maxI0 = deFloorFloatToInt32(uBounds0.y() - 0.5f);
1265     const int minI1 = deFloorFloatToInt32(uBounds1.x() - 0.5f);
1266     const int maxI1 = deFloorFloatToInt32(uBounds1.y() - 0.5f);
1267 
1268     for (int i0 = minI0; i0 <= maxI0; i0++)
1269     {
1270         const float minA0 = de::clamp((uBounds0.x() - 0.5f) - float(i0), 0.0f, 1.0f);
1271         const float maxA0 = de::clamp((uBounds0.y() - 0.5f) - float(i0), 0.0f, 1.0f);
1272         const Vec2 ptA0   = Vec2(minA0, maxA0);
1273         Vec4 depths;
1274 
1275         {
1276             const int x0 = wrap(sampler.wrapS, i0, w0);
1277             const int x1 = wrap(sampler.wrapS, i0 + 1, w0);
1278 
1279             depths[0] = lookupDepth(level0, sampler, x0, coordZ, 0);
1280             depths[1] = lookupDepth(level0, sampler, x1, coordZ, 0);
1281         }
1282 
1283         for (int i1 = minI1; i1 <= maxI1; i1++)
1284         {
1285             const float minA1 = de::clamp((uBounds1.x() - 0.5f) - float(i1), 0.0f, 1.0f);
1286             const float maxA1 = de::clamp((uBounds1.y() - 0.5f) - float(i1), 0.0f, 1.0f);
1287             const Vec2 ptA1   = Vec2(minA1, maxA1);
1288 
1289             {
1290                 const int x0 = wrap(sampler.wrapS, i1, w1);
1291                 const int x1 = wrap(sampler.wrapS, i1 + 1, w1);
1292 
1293                 depths[2] = lookupDepth(level1, sampler, x0, coordZ, 0);
1294                 depths[3] = lookupDepth(level1, sampler, x1, coordZ, 0);
1295             }
1296 
1297             if (isBilinearCompareValid(sampler.compare, prec, depths, ptA0, ptA1, cmpReference, result,
1298                                        isFixedPointDepth))
1299                 return true;
1300         }
1301     }
1302 
1303     return false;
1304 }
1305 
isMipmapLinearCompareResultValid(const ConstPixelBufferAccess & level0,const ConstPixelBufferAccess & level1,const Sampler & sampler,const Sampler::FilterMode levelFilter,const TexComparePrecision & prec,const Vec1 & coord,const int coordZ,const Vec2 & fBounds,const float cmpReference,const float result)1306 static bool isMipmapLinearCompareResultValid(const ConstPixelBufferAccess &level0, const ConstPixelBufferAccess &level1,
1307                                              const Sampler &sampler, const Sampler::FilterMode levelFilter,
1308                                              const TexComparePrecision &prec, const Vec1 &coord, const int coordZ,
1309                                              const Vec2 &fBounds, const float cmpReference, const float result)
1310 {
1311     if (levelFilter == Sampler::LINEAR)
1312         return isLinearMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds,
1313                                                       cmpReference, result);
1314     else
1315         return isNearestMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds,
1316                                                        cmpReference, result);
1317 }
1318 
isTexCompareResultValid(const TextureCubeView & texture,const Sampler & sampler,const TexComparePrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const float cmpReference,const float result)1319 bool isTexCompareResultValid(const TextureCubeView &texture, const Sampler &sampler, const TexComparePrecision &prec,
1320                              const Vec3 &coord, const Vec2 &lodBounds, const float cmpReference, const float result)
1321 {
1322     int numPossibleFaces = 0;
1323     CubeFace possibleFaces[CUBEFACE_LAST];
1324 
1325     DE_ASSERT(isSamplerSupported(sampler));
1326 
1327     getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
1328 
1329     if (numPossibleFaces == 0)
1330         return true; // Result is undefined.
1331 
1332     for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
1333     {
1334         const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx],
1335                                              projectToFace(possibleFaces[tryFaceNdx], coord));
1336         const float minLod        = lodBounds.x();
1337         const float maxLod        = lodBounds.y();
1338         const bool canBeMagnified = minLod <= sampler.lodThreshold;
1339         const bool canBeMinified  = maxLod > sampler.lodThreshold;
1340 
1341         if (canBeMagnified)
1342         {
1343             if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.magFilter, prec, faceCoords, cmpReference,
1344                                               result))
1345                 return true;
1346         }
1347 
1348         if (canBeMinified)
1349         {
1350             const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
1351             const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
1352             const int minTexLevel      = 0;
1353             const int maxTexLevel      = texture.getNumLevels() - 1;
1354 
1355             DE_ASSERT(minTexLevel < maxTexLevel);
1356 
1357             if (isLinearMipmap)
1358             {
1359                 const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
1360                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
1361 
1362                 DE_ASSERT(minLevel <= maxLevel);
1363 
1364                 for (int level = minLevel; level <= maxLevel; level++)
1365                 {
1366                     const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
1367                     const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
1368 
1369                     if (isCubeMipmapLinearCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter),
1370                                                              prec, faceCoords, Vec2(minF, maxF), cmpReference, result))
1371                         return true;
1372                 }
1373             }
1374             else if (isNearestMipmap)
1375             {
1376                 // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1377                 //         decision to allow floor(lod + 0.5) as well.
1378                 const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
1379                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
1380 
1381                 DE_ASSERT(minLevel <= maxLevel);
1382 
1383                 for (int level = minLevel; level <= maxLevel; level++)
1384                 {
1385                     if (isCubeLevelCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter), prec,
1386                                                       faceCoords, cmpReference, result))
1387                         return true;
1388                 }
1389             }
1390             else
1391             {
1392                 if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.minFilter, prec, faceCoords,
1393                                                   cmpReference, result))
1394                     return true;
1395             }
1396         }
1397     }
1398 
1399     return false;
1400 }
1401 
isTexCompareResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const TexComparePrecision & prec,const Vec3 & coord,const Vec2 & lodBounds,const float cmpReference,const float result)1402 bool isTexCompareResultValid(const Texture2DArrayView &texture, const Sampler &sampler, const TexComparePrecision &prec,
1403                              const Vec3 &coord, const Vec2 &lodBounds, const float cmpReference, const float result)
1404 {
1405     const float depthErr =
1406         computeFloatingPointError(coord.z(), prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z());
1407     const float minZ   = coord.z() - depthErr;
1408     const float maxZ   = coord.z() + depthErr;
1409     const int minLayer = de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers() - 1);
1410     const int maxLayer = de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers() - 1);
1411 
1412     DE_ASSERT(isSamplerSupported(sampler));
1413 
1414     for (int layer = minLayer; layer <= maxLayer; layer++)
1415     {
1416         const float minLod        = lodBounds.x();
1417         const float maxLod        = lodBounds.y();
1418         const bool canBeMagnified = minLod <= sampler.lodThreshold;
1419         const bool canBeMinified  = maxLod > sampler.lodThreshold;
1420 
1421         if (canBeMagnified)
1422         {
1423             if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord.swizzle(0, 1),
1424                                           layer, cmpReference, result))
1425                 return true;
1426         }
1427 
1428         if (canBeMinified)
1429         {
1430             const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
1431             const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
1432             const int minTexLevel      = 0;
1433             const int maxTexLevel      = texture.getNumLevels() - 1;
1434 
1435             DE_ASSERT(minTexLevel < maxTexLevel);
1436 
1437             if (isLinearMipmap)
1438             {
1439                 const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
1440                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
1441 
1442                 DE_ASSERT(minLevel <= maxLevel);
1443 
1444                 for (int level = minLevel; level <= maxLevel; level++)
1445                 {
1446                     const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
1447                     const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
1448 
1449                     if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler,
1450                                                          getLevelFilter(sampler.minFilter), prec, coord.swizzle(0, 1),
1451                                                          layer, Vec2(minF, maxF), cmpReference, result))
1452                         return true;
1453                 }
1454             }
1455             else if (isNearestMipmap)
1456             {
1457                 // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1458                 //         decision to allow floor(lod + 0.5) as well.
1459                 const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
1460                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
1461 
1462                 DE_ASSERT(minLevel <= maxLevel);
1463 
1464                 for (int level = minLevel; level <= maxLevel; level++)
1465                 {
1466                     if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter),
1467                                                   prec, coord.swizzle(0, 1), layer, cmpReference, result))
1468                         return true;
1469                 }
1470             }
1471             else
1472             {
1473                 if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec,
1474                                               coord.swizzle(0, 1), layer, cmpReference, result))
1475                     return true;
1476             }
1477         }
1478     }
1479 
1480     return false;
1481 }
1482 
isTexCompareResultValid(const Texture1DView & texture,const Sampler & sampler,const TexComparePrecision & prec,const Vec1 & coord,const Vec2 & lodBounds,const float cmpReference,const float result)1483 bool isTexCompareResultValid(const Texture1DView &texture, const Sampler &sampler, const TexComparePrecision &prec,
1484                              const Vec1 &coord, const Vec2 &lodBounds, const float cmpReference, const float result)
1485 {
1486     const float minLod        = lodBounds.x();
1487     const float maxLod        = lodBounds.y();
1488     const bool canBeMagnified = minLod <= sampler.lodThreshold;
1489     const bool canBeMinified  = maxLod > sampler.lodThreshold;
1490 
1491     DE_ASSERT(isSamplerSupported(sampler));
1492 
1493     if (canBeMagnified)
1494     {
1495         if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, cmpReference,
1496                                       result))
1497             return true;
1498     }
1499 
1500     if (canBeMinified)
1501     {
1502         const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
1503         const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
1504         const int minTexLevel      = 0;
1505         const int maxTexLevel      = texture.getNumLevels() - 1;
1506 
1507         DE_ASSERT(minTexLevel < maxTexLevel);
1508 
1509         if (isLinearMipmap)
1510         {
1511             const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
1512             const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
1513 
1514             DE_ASSERT(minLevel <= maxLevel);
1515 
1516             for (int level = minLevel; level <= maxLevel; level++)
1517             {
1518                 const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
1519                 const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
1520 
1521                 if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler,
1522                                                      getLevelFilter(sampler.minFilter), prec, coord, 0,
1523                                                      Vec2(minF, maxF), cmpReference, result))
1524                     return true;
1525             }
1526         }
1527         else if (isNearestMipmap)
1528         {
1529             // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1530             //         decision to allow floor(lod + 0.5) as well.
1531             const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
1532             const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
1533 
1534             DE_ASSERT(minLevel <= maxLevel);
1535 
1536             for (int level = minLevel; level <= maxLevel; level++)
1537             {
1538                 if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec,
1539                                               coord, 0, cmpReference, result))
1540                     return true;
1541             }
1542         }
1543         else
1544         {
1545             if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, cmpReference,
1546                                           result))
1547                 return true;
1548         }
1549     }
1550 
1551     return false;
1552 }
1553 
isTexCompareResultValid(const Texture1DArrayView & texture,const Sampler & sampler,const TexComparePrecision & prec,const Vec2 & coord,const Vec2 & lodBounds,const float cmpReference,const float result)1554 bool isTexCompareResultValid(const Texture1DArrayView &texture, const Sampler &sampler, const TexComparePrecision &prec,
1555                              const Vec2 &coord, const Vec2 &lodBounds, const float cmpReference, const float result)
1556 {
1557     const float depthErr = computeFloatingPointError(coord.y(), prec.coordBits.y()) +
1558                            computeFixedPointError(prec.uvwBits.y()); //\todo: should we go with y in prec?
1559     const float minZ   = coord.y() - depthErr;
1560     const float maxZ   = coord.y() + depthErr;
1561     const int minLayer = de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers() - 1);
1562     const int maxLayer = de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers() - 1);
1563 
1564     DE_ASSERT(isSamplerSupported(sampler));
1565 
1566     for (int layer = minLayer; layer <= maxLayer; layer++)
1567     {
1568         const float minLod        = lodBounds.x();
1569         const float maxLod        = lodBounds.y();
1570         const bool canBeMagnified = minLod <= sampler.lodThreshold;
1571         const bool canBeMinified  = maxLod > sampler.lodThreshold;
1572 
1573         if (canBeMagnified)
1574         {
1575             if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, Vec1(coord.x()), layer,
1576                                           cmpReference, result))
1577                 return true;
1578         }
1579 
1580         if (canBeMinified)
1581         {
1582             const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
1583             const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
1584             const int minTexLevel      = 0;
1585             const int maxTexLevel      = texture.getNumLevels() - 1;
1586 
1587             DE_ASSERT(minTexLevel < maxTexLevel);
1588 
1589             if (isLinearMipmap)
1590             {
1591                 const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
1592                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
1593 
1594                 DE_ASSERT(minLevel <= maxLevel);
1595 
1596                 for (int level = minLevel; level <= maxLevel; level++)
1597                 {
1598                     const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
1599                     const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
1600 
1601                     if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level + 1), sampler,
1602                                                          getLevelFilter(sampler.minFilter), prec, Vec1(coord.x()),
1603                                                          layer, Vec2(minF, maxF), cmpReference, result))
1604                         return true;
1605                 }
1606             }
1607             else if (isNearestMipmap)
1608             {
1609                 // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1610                 //         decision to allow floor(lod + 0.5) as well.
1611                 const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
1612                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
1613 
1614                 DE_ASSERT(minLevel <= maxLevel);
1615 
1616                 for (int level = minLevel; level <= maxLevel; level++)
1617                 {
1618                     if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter),
1619                                                   prec, Vec1(coord.x()), layer, cmpReference, result))
1620                         return true;
1621                 }
1622             }
1623             else
1624             {
1625                 if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, Vec1(coord.x()),
1626                                               layer, cmpReference, result))
1627                     return true;
1628             }
1629         }
1630     }
1631 
1632     return false;
1633 }
1634 
isTexCompareResultValid(const TextureCubeArrayView & texture,const Sampler & sampler,const TexComparePrecision & prec,const Vec4 & coord,const Vec2 & lodBounds,const float cmpReference,const float result)1635 bool isTexCompareResultValid(const TextureCubeArrayView &texture, const Sampler &sampler,
1636                              const TexComparePrecision &prec, const Vec4 &coord, const Vec2 &lodBounds,
1637                              const float cmpReference, const float result)
1638 {
1639     const Vec3 coord3    = coord.swizzle(0, 1, 2);
1640     int numPossibleFaces = 0;
1641     CubeFace possibleFaces[CUBEFACE_LAST];
1642 
1643     DE_ASSERT(isSamplerSupported(sampler));
1644 
1645     getPossibleCubeFaces(coord3, prec.coordBits, &possibleFaces[0], numPossibleFaces);
1646 
1647     if (numPossibleFaces == 0)
1648         return true; // Result is undefined.
1649 
1650     for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
1651     {
1652         const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx],
1653                                              projectToFace(possibleFaces[tryFaceNdx], coord3));
1654         const float minLod        = lodBounds.x();
1655         const float maxLod        = lodBounds.y();
1656         const bool canBeMagnified = minLod <= sampler.lodThreshold;
1657         const bool canBeMinified  = maxLod > sampler.lodThreshold;
1658 
1659         if (canBeMagnified)
1660         {
1661             if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.magFilter, prec, faceCoords, coord.w(),
1662                                               cmpReference, result))
1663                 return true;
1664         }
1665 
1666         if (canBeMinified)
1667         {
1668             const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter);
1669             const bool isLinearMipmap  = isLinearMipmapFilter(sampler.minFilter);
1670             const int minTexLevel      = 0;
1671             const int maxTexLevel      = texture.getNumLevels() - 1;
1672 
1673             DE_ASSERT(minTexLevel < maxTexLevel);
1674 
1675             if (isLinearMipmap)
1676             {
1677                 const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel - 1);
1678                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel - 1);
1679 
1680                 DE_ASSERT(minLevel <= maxLevel);
1681 
1682                 for (int level = minLevel; level <= maxLevel; level++)
1683                 {
1684                     const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f);
1685                     const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f);
1686 
1687                     if (isCubeMipmapLinearCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter),
1688                                                              prec, faceCoords, coord.w(), Vec2(minF, maxF),
1689                                                              cmpReference, result))
1690                         return true;
1691                 }
1692             }
1693             else if (isNearestMipmap)
1694             {
1695                 // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made
1696                 //         decision to allow floor(lod + 0.5) as well.
1697                 const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel);
1698                 const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel);
1699 
1700                 DE_ASSERT(minLevel <= maxLevel);
1701 
1702                 for (int level = minLevel; level <= maxLevel; level++)
1703                 {
1704                     if (isCubeLevelCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter), prec,
1705                                                       faceCoords, coord.w(), cmpReference, result))
1706                         return true;
1707                 }
1708             }
1709             else
1710             {
1711                 if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.minFilter, prec, faceCoords, coord.w(),
1712                                                   cmpReference, result))
1713                     return true;
1714             }
1715         }
1716     }
1717 
1718     return false;
1719 }
1720 
isGatherOffsetsCompareResultValid(const ConstPixelBufferAccess & texture,const Sampler & sampler,const TexComparePrecision & prec,const Vec2 & coord,int coordZ,const IVec2 (& offsets)[4],float cmpReference,const Vec4 & result)1721 static bool isGatherOffsetsCompareResultValid(const ConstPixelBufferAccess &texture, const Sampler &sampler,
1722                                               const TexComparePrecision &prec, const Vec2 &coord, int coordZ,
1723                                               const IVec2 (&offsets)[4], float cmpReference, const Vec4 &result)
1724 {
1725     const bool isFixedPointDepth = isFixedPointDepthTextureFormat(texture.getFormat());
1726     const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, texture.getWidth(), coord.x(),
1727                                                          prec.coordBits.x(), prec.uvwBits.x());
1728     const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, texture.getHeight(), coord.y(),
1729                                                          prec.coordBits.y(), prec.uvwBits.y());
1730 
1731     // Integer coordinate bounds for (x0, y0) - without wrap mode
1732     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
1733     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
1734     const int minJ = deFloorFloatToInt32(vBounds.x() - 0.5f);
1735     const int maxJ = deFloorFloatToInt32(vBounds.y() - 0.5f);
1736 
1737     const int w = texture.getWidth();
1738     const int h = texture.getHeight();
1739 
1740     for (int j = minJ; j <= maxJ; j++)
1741     {
1742         for (int i = minI; i <= maxI; i++)
1743         {
1744             bool isCurrentPixelValid = true;
1745 
1746             for (int offNdx = 0; offNdx < 4 && isCurrentPixelValid; offNdx++)
1747             {
1748                 // offNdx-th coordinate offset and then wrapped.
1749                 const int x       = wrap(sampler.wrapS, i + offsets[offNdx].x(), w);
1750                 const int y       = wrap(sampler.wrapT, j + offsets[offNdx].y(), h);
1751                 const float depth = lookupDepth(texture, sampler, x, y, coordZ);
1752                 const CmpResultSet resSet =
1753                     execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth);
1754 
1755                 if (!isResultInSet(resSet, result[offNdx], prec.resultBits))
1756                     isCurrentPixelValid = false;
1757             }
1758 
1759             if (isCurrentPixelValid)
1760                 return true;
1761         }
1762     }
1763 
1764     return false;
1765 }
1766 
isGatherOffsetsCompareResultValid(const Texture2DView & texture,const Sampler & sampler,const TexComparePrecision & prec,const Vec2 & coord,const IVec2 (& offsets)[4],float cmpReference,const Vec4 & result)1767 bool isGatherOffsetsCompareResultValid(const Texture2DView &texture, const Sampler &sampler,
1768                                        const TexComparePrecision &prec, const Vec2 &coord, const IVec2 (&offsets)[4],
1769                                        float cmpReference, const Vec4 &result)
1770 {
1771     DE_ASSERT(isSamplerSupported(sampler));
1772 
1773     return isGatherOffsetsCompareResultValid(texture.getLevel(0), sampler, prec, coord, 0, offsets, cmpReference,
1774                                              result);
1775 }
1776 
isGatherOffsetsCompareResultValid(const Texture2DArrayView & texture,const Sampler & sampler,const TexComparePrecision & prec,const Vec3 & coord,const IVec2 (& offsets)[4],float cmpReference,const Vec4 & result)1777 bool isGatherOffsetsCompareResultValid(const Texture2DArrayView &texture, const Sampler &sampler,
1778                                        const TexComparePrecision &prec, const Vec3 &coord, const IVec2 (&offsets)[4],
1779                                        float cmpReference, const Vec4 &result)
1780 {
1781     const float depthErr =
1782         computeFloatingPointError(coord.z(), prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z());
1783     const float minZ   = coord.z() - depthErr;
1784     const float maxZ   = coord.z() + depthErr;
1785     const int minLayer = de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers() - 1);
1786     const int maxLayer = de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers() - 1);
1787 
1788     DE_ASSERT(isSamplerSupported(sampler));
1789 
1790     for (int layer = minLayer; layer <= maxLayer; layer++)
1791     {
1792         if (isGatherOffsetsCompareResultValid(texture.getLevel(0), sampler, prec, coord.swizzle(0, 1), layer, offsets,
1793                                               cmpReference, result))
1794             return true;
1795     }
1796     return false;
1797 }
1798 
isGatherCompareResultValid(const TextureCubeView & texture,const Sampler & sampler,const TexComparePrecision & prec,const CubeFaceFloatCoords & coords,float cmpReference,const Vec4 & result)1799 static bool isGatherCompareResultValid(const TextureCubeView &texture, const Sampler &sampler,
1800                                        const TexComparePrecision &prec, const CubeFaceFloatCoords &coords,
1801                                        float cmpReference, const Vec4 &result)
1802 {
1803     const bool isFixedPointDepth = isFixedPointDepthTextureFormat(texture.getLevelFace(0, coords.face).getFormat());
1804     const int size               = texture.getLevelFace(0, coords.face).getWidth();
1805     const Vec2 uBounds =
1806         computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x());
1807     const Vec2 vBounds =
1808         computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y());
1809 
1810     // Integer coordinate bounds for (x0,y0) - without wrap mode
1811     const int minI = deFloorFloatToInt32(uBounds.x() - 0.5f);
1812     const int maxI = deFloorFloatToInt32(uBounds.y() - 0.5f);
1813     const int minJ = deFloorFloatToInt32(vBounds.x() - 0.5f);
1814     const int maxJ = deFloorFloatToInt32(vBounds.y() - 0.5f);
1815 
1816     // Face accesses
1817     ConstPixelBufferAccess faces[CUBEFACE_LAST];
1818     for (int face = 0; face < CUBEFACE_LAST; face++)
1819         faces[face] = texture.getLevelFace(0, CubeFace(face));
1820 
1821     for (int j = minJ; j <= maxJ; j++)
1822     {
1823         for (int i = minI; i <= maxI; i++)
1824         {
1825             static const IVec2 offsets[4] = {IVec2(0, 1), IVec2(1, 1), IVec2(1, 0), IVec2(0, 0)};
1826 
1827             bool isCurrentPixelValid = true;
1828 
1829             for (int offNdx = 0; offNdx < 4 && isCurrentPixelValid; offNdx++)
1830             {
1831                 const CubeFaceIntCoords c = remapCubeEdgeCoords(
1832                     CubeFaceIntCoords(coords.face, i + offsets[offNdx].x(), j + offsets[offNdx].y()), size);
1833                 // If any of samples is out of both edges, implementations can do pretty much anything according to spec.
1834                 // \todo [2014-06-05 nuutti] Test the special case where all corner pixels have exactly the same color.
1835                 //                             See also isSeamlessLinearCompareResultValid and similar.
1836                 if (c.face == CUBEFACE_LAST)
1837                     return true;
1838 
1839                 const float depth = lookupDepthNoBorder(faces[c.face], sampler, c.s, c.t);
1840                 const CmpResultSet resSet =
1841                     execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth);
1842 
1843                 if (!isResultInSet(resSet, result[offNdx], prec.resultBits))
1844                     isCurrentPixelValid = false;
1845             }
1846 
1847             if (isCurrentPixelValid)
1848                 return true;
1849         }
1850     }
1851 
1852     return false;
1853 }
1854 
isGatherCompareResultValid(const TextureCubeView & texture,const Sampler & sampler,const TexComparePrecision & prec,const Vec3 & coord,float cmpReference,const Vec4 & result)1855 bool isGatherCompareResultValid(const TextureCubeView &texture, const Sampler &sampler, const TexComparePrecision &prec,
1856                                 const Vec3 &coord, float cmpReference, const Vec4 &result)
1857 {
1858     int numPossibleFaces = 0;
1859     CubeFace possibleFaces[CUBEFACE_LAST];
1860 
1861     DE_ASSERT(isSamplerSupported(sampler));
1862 
1863     getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces);
1864 
1865     if (numPossibleFaces == 0)
1866         return true; // Result is undefined.
1867 
1868     for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++)
1869     {
1870         const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx],
1871                                              projectToFace(possibleFaces[tryFaceNdx], coord));
1872 
1873         if (isGatherCompareResultValid(texture, sampler, prec, faceCoords, cmpReference, result))
1874             return true;
1875     }
1876 
1877     return false;
1878 }
1879 
1880 } // namespace tcu
1881