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