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 30namespace tcu 31{ 32 33using namespace TexVerifierUtil; 34 35// Generic utilities 36 37#if defined(DE_DEBUG) 38static bool isSamplerSupported (const Sampler& sampler) 39{ 40 return sampler.compare != Sampler::COMPAREMODE_NONE && 41 isWrapModeSupported(sampler.wrapS) && 42 isWrapModeSupported(sampler.wrapT) && 43 isWrapModeSupported(sampler.wrapR); 44} 45#endif // DE_DEBUG 46 47struct CmpResultSet 48{ 49 bool isTrue; 50 bool isFalse; 51 52 CmpResultSet (void) 53 : isTrue (false) 54 , isFalse (false) 55 { 56 } 57}; 58 59static CmpResultSet execCompare (const Sampler::CompareMode compareMode, 60 const float cmpValue_, 61 const float cmpReference_, 62 const int referenceBits, 63 const bool isFixedPoint) 64{ 65 const bool clampValues = isFixedPoint; // if comparing against a floating point texture, ref (and value) is not clamped 66 const float cmpValue = (clampValues) ? (de::clamp(cmpValue_, 0.0f, 1.0f)) : (cmpValue_); 67 const float cmpReference = (clampValues) ? (de::clamp(cmpReference_, 0.0f, 1.0f)) : (cmpReference_); 68 const float err = computeFixedPointError(referenceBits); 69 CmpResultSet res; 70 71 switch (compareMode) 72 { 73 case Sampler::COMPAREMODE_LESS: 74 res.isTrue = cmpReference-err < cmpValue; 75 res.isFalse = cmpReference+err >= cmpValue; 76 break; 77 78 case Sampler::COMPAREMODE_LESS_OR_EQUAL: 79 res.isTrue = cmpReference-err <= cmpValue; 80 res.isFalse = cmpReference+err > cmpValue; 81 break; 82 83 case Sampler::COMPAREMODE_GREATER: 84 res.isTrue = cmpReference+err > cmpValue; 85 res.isFalse = cmpReference-err <= cmpValue; 86 break; 87 88 case Sampler::COMPAREMODE_GREATER_OR_EQUAL: 89 res.isTrue = cmpReference+err >= cmpValue; 90 res.isFalse = cmpReference-err < cmpValue; 91 break; 92 93 case Sampler::COMPAREMODE_EQUAL: 94 res.isTrue = de::inRange(cmpValue, cmpReference-err, cmpReference+err); 95 res.isFalse = err != 0.0f || cmpValue != cmpReference; 96 break; 97 98 case Sampler::COMPAREMODE_NOT_EQUAL: 99 res.isTrue = err != 0.0f || cmpValue != cmpReference; 100 res.isFalse = de::inRange(cmpValue, cmpReference-err, cmpReference+err); 101 break; 102 103 case Sampler::COMPAREMODE_ALWAYS: 104 res.isTrue = true; 105 break; 106 107 case Sampler::COMPAREMODE_NEVER: 108 res.isFalse = true; 109 break; 110 111 default: 112 DE_ASSERT(false); 113 } 114 115 DE_ASSERT(res.isTrue || res.isFalse); 116 return res; 117} 118 119static inline bool isResultInSet (const CmpResultSet resultSet, const float result, const int resultBits) 120{ 121 const float err = computeFixedPointError(resultBits); 122 const float minR = result-err; 123 const float maxR = result+err; 124 125 return (resultSet.isTrue && de::inRange(1.0f, minR, maxR)) || 126 (resultSet.isFalse && de::inRange(0.0f, minR, maxR)); 127} 128 129static inline bool coordsInBounds (const ConstPixelBufferAccess& access, int x, int y, int z) 130{ 131 return de::inBounds(x, 0, access.getWidth()) && de::inBounds(y, 0, access.getHeight()) && de::inBounds(z, 0, access.getDepth()); 132} 133 134static float lookupDepth (const tcu::ConstPixelBufferAccess& access, const Sampler& sampler, int i, int j, int k) 135{ 136 if (coordsInBounds(access, i, j, k)) 137 return access.getPixDepth(i, j, k); 138 else 139 return sampleTextureBorder<float>(access.getFormat(), sampler).x(); 140} 141 142// lookup depth value at a point that is guaranteed to not sample border such as cube map faces. 143static float lookupDepthNoBorder (const tcu::ConstPixelBufferAccess& access, const Sampler& sampler, int i, int j, int k = 0) 144{ 145 DE_UNREF(sampler); 146 DE_ASSERT(coordsInBounds(access, i, j, k)); 147 return access.getPixDepth(i, j, k); 148} 149 150// Values are in order (0,0), (1,0), (0,1), (1,1) 151static float bilinearInterpolate (const Vec4& values, const float x, const float y) 152{ 153 const float v00 = values[0]; 154 const float v10 = values[1]; 155 const float v01 = values[2]; 156 const float v11 = values[3]; 157 const float res = v00*(1.0f-x)*(1.0f-y) + v10*x*(1.0f-y) + v01*(1.0f-x)*y + v11*x*y; 158 return res; 159} 160 161static bool isFixedPointDepthTextureFormat (const tcu::TextureFormat& format) 162{ 163 const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(format.type); 164 165 if (format.order == TextureFormat::D) 166 { 167 // depth internal formats cannot be non-normalized integers 168 return channelClass != tcu::TEXTURECHANNELCLASS_FLOATING_POINT; 169 } 170 else if (format.order == TextureFormat::DS) 171 { 172 // combined formats have no single channel class, detect format manually 173 switch (format.type) 174 { 175 case tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV: return false; 176 case tcu::TextureFormat::UNSIGNED_INT_24_8: return true; 177 178 default: 179 { 180 // unknown format 181 DE_ASSERT(false); 182 return true; 183 } 184 } 185 } 186 187 return false; 188} 189 190static bool isLinearCompareValid (const Sampler::CompareMode compareMode, 191 const TexComparePrecision& prec, 192 const Vec2& depths, 193 const Vec2& fBounds, 194 const float cmpReference, 195 const float result, 196 const bool isFixedPointDepth) 197{ 198 DE_ASSERT(0.0f <= fBounds.x() && fBounds.x() <= fBounds.y() && fBounds.y() <= 1.0f); 199 200 const float d0 = depths[0]; 201 const float d1 = depths[1]; 202 203 const CmpResultSet cmp0 = execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth); 204 const CmpResultSet cmp1 = execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth); 205 206 const deUint32 isTrue = (deUint32(cmp0.isTrue)<<0) 207 | (deUint32(cmp1.isTrue)<<1); 208 const deUint32 isFalse = (deUint32(cmp0.isFalse)<<0) 209 | (deUint32(cmp1.isFalse)<<1); 210 211 // Interpolation parameters 212 const float f0 = fBounds.x(); 213 const float f1 = fBounds.y(); 214 215 // Error parameters 216 const float pcfErr = computeFixedPointError(prec.pcfBits); 217 const float resErr = computeFixedPointError(prec.resultBits); 218 const float totalErr = pcfErr+resErr; 219 220 // Iterate over all valid combinations. 221 for (deUint32 comb = 0; comb < (1<<2); comb++) 222 { 223 // Filter out invalid combinations. 224 if (((comb & isTrue) | (~comb & isFalse)) != (1<<2)-1) 225 continue; 226 227 const bool cmp0True = ((comb>>0)&1) != 0; 228 const bool cmp1True = ((comb>>1)&1) != 0; 229 230 const float ref0 = cmp0True ? 1.0f : 0.0f; 231 const float ref1 = cmp1True ? 1.0f : 0.0f; 232 233 const float v0 = ref0*(1.0f-f0) + ref1*f0; 234 const float v1 = ref0*(1.0f-f1) + ref1*f1; 235 const float minV = de::min(v0, v1); 236 const float maxV = de::max(v0, v1); 237 const float minR = minV-totalErr; 238 const float maxR = maxV+totalErr; 239 240 if (de::inRange(result, minR, maxR)) 241 return true; 242 } 243 244 return false; 245} 246 247static inline BVec4 extractBVec4 (const deUint32 val, int offset) 248{ 249 return BVec4(((val>>(offset+0))&1) != 0, 250 ((val>>(offset+1))&1) != 0, 251 ((val>>(offset+2))&1) != 0, 252 ((val>>(offset+3))&1) != 0); 253} 254 255static bool isBilinearAnyCompareValid (const Sampler::CompareMode compareMode, 256 const TexComparePrecision& prec, 257 const Vec4& depths, 258 const float cmpReference, 259 const float result, 260 const bool isFixedPointDepth) 261{ 262 DE_ASSERT(prec.pcfBits == 0); 263 264 const float d0 = depths[0]; 265 const float d1 = depths[1]; 266 const float d2 = depths[2]; 267 const float d3 = depths[3]; 268 269 const CmpResultSet cmp0 = execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth); 270 const CmpResultSet cmp1 = execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth); 271 const CmpResultSet cmp2 = execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth); 272 const CmpResultSet cmp3 = execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth); 273 274 const bool canBeTrue = cmp0.isTrue || cmp1.isTrue || cmp2.isTrue || cmp3.isTrue; 275 const bool canBeFalse = cmp0.isFalse || cmp1.isFalse || cmp2.isFalse || cmp3.isFalse; 276 277 const float resErr = computeFixedPointError(prec.resultBits); 278 279 const float minBound = canBeFalse ? 0.0f : 1.0f; 280 const float maxBound = canBeTrue ? 1.0f : 0.0f; 281 282 return de::inRange(result, minBound-resErr, maxBound+resErr); 283} 284 285static bool isBilinearPCFCompareValid (const Sampler::CompareMode compareMode, 286 const TexComparePrecision& prec, 287 const Vec4& depths, 288 const Vec2& xBounds, 289 const Vec2& yBounds, 290 const float cmpReference, 291 const float result, 292 const bool isFixedPointDepth) 293{ 294 DE_ASSERT(0.0f <= xBounds.x() && xBounds.x() <= xBounds.y() && xBounds.y() <= 1.0f); 295 DE_ASSERT(0.0f <= yBounds.x() && yBounds.x() <= yBounds.y() && yBounds.y() <= 1.0f); 296 DE_ASSERT(prec.pcfBits > 0); 297 298 const float d0 = depths[0]; 299 const float d1 = depths[1]; 300 const float d2 = depths[2]; 301 const float d3 = depths[3]; 302 303 const CmpResultSet cmp0 = execCompare(compareMode, d0, cmpReference, prec.referenceBits, isFixedPointDepth); 304 const CmpResultSet cmp1 = execCompare(compareMode, d1, cmpReference, prec.referenceBits, isFixedPointDepth); 305 const CmpResultSet cmp2 = execCompare(compareMode, d2, cmpReference, prec.referenceBits, isFixedPointDepth); 306 const CmpResultSet cmp3 = execCompare(compareMode, d3, cmpReference, prec.referenceBits, isFixedPointDepth); 307 308 const deUint32 isTrue = (deUint32(cmp0.isTrue)<<0) 309 | (deUint32(cmp1.isTrue)<<1) 310 | (deUint32(cmp2.isTrue)<<2) 311 | (deUint32(cmp3.isTrue)<<3); 312 const deUint32 isFalse = (deUint32(cmp0.isFalse)<<0) 313 | (deUint32(cmp1.isFalse)<<1) 314 | (deUint32(cmp2.isFalse)<<2) 315 | (deUint32(cmp3.isFalse)<<3); 316 317 // Interpolation parameters 318 const float x0 = xBounds.x(); 319 const float x1 = xBounds.y(); 320 const float y0 = yBounds.x(); 321 const float y1 = yBounds.y(); 322 323 // Error parameters 324 const float pcfErr = computeFixedPointError(prec.pcfBits); 325 const float resErr = computeFixedPointError(prec.resultBits); 326 const float totalErr = pcfErr+resErr; 327 328 // Iterate over all valid combinations. 329 // \note It is not enough to compute minmax over all possible result sets, as ranges may 330 // not necessarily overlap, i.e. there are gaps between valid ranges. 331 for (deUint32 comb = 0; comb < (1<<4); comb++) 332 { 333 // Filter out invalid combinations: 334 // 1) True bit is set in comb but not in isTrue => sample can not be true 335 // 2) True bit is NOT set in comb and not in isFalse => sample can not be false 336 if (((comb & isTrue) | (~comb & isFalse)) != (1<<4)-1) 337 continue; 338 339 const BVec4 cmpTrue = extractBVec4(comb, 0); 340 const Vec4 refVal = select(Vec4(1.0f), Vec4(0.0f), cmpTrue); 341 342 const float v0 = bilinearInterpolate(refVal, x0, y0); 343 const float v1 = bilinearInterpolate(refVal, x1, y0); 344 const float v2 = bilinearInterpolate(refVal, x0, y1); 345 const float v3 = bilinearInterpolate(refVal, x1, y1); 346 const float minV = de::min(v0, de::min(v1, de::min(v2, v3))); 347 const float maxV = de::max(v0, de::max(v1, de::max(v2, v3))); 348 const float minR = minV-totalErr; 349 const float maxR = maxV+totalErr; 350 351 if (de::inRange(result, minR, maxR)) 352 return true; 353 } 354 355 return false; 356} 357 358static bool isBilinearCompareValid (const Sampler::CompareMode compareMode, 359 const TexComparePrecision& prec, 360 const Vec4& depths, 361 const Vec2& xBounds, 362 const Vec2& yBounds, 363 const float cmpReference, 364 const float result, 365 const bool isFixedPointDepth) 366{ 367 if (prec.pcfBits > 0) 368 return isBilinearPCFCompareValid(compareMode, prec, depths, xBounds, yBounds, cmpReference, result, isFixedPointDepth); 369 else 370 return isBilinearAnyCompareValid(compareMode, prec, depths, cmpReference, result, isFixedPointDepth); 371} 372 373static bool isTrilinearAnyCompareValid (const Sampler::CompareMode compareMode, 374 const TexComparePrecision& prec, 375 const Vec4& depths0, 376 const Vec4& depths1, 377 const float cmpReference, 378 const float result, 379 const bool isFixedPointDepth) 380{ 381 DE_ASSERT(prec.pcfBits == 0); 382 383 const CmpResultSet cmp00 = execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth); 384 const CmpResultSet cmp01 = execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth); 385 const CmpResultSet cmp02 = execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth); 386 const CmpResultSet cmp03 = execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth); 387 388 const CmpResultSet cmp10 = execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth); 389 const CmpResultSet cmp11 = execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth); 390 const CmpResultSet cmp12 = execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth); 391 const CmpResultSet cmp13 = execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth); 392 393 const bool canBeTrue = cmp00.isTrue || 394 cmp01.isTrue || 395 cmp02.isTrue || 396 cmp03.isTrue || 397 cmp10.isTrue || 398 cmp11.isTrue || 399 cmp12.isTrue || 400 cmp13.isTrue; 401 const bool canBeFalse = cmp00.isFalse || 402 cmp01.isFalse || 403 cmp02.isFalse || 404 cmp03.isFalse || 405 cmp10.isFalse || 406 cmp11.isFalse || 407 cmp12.isFalse || 408 cmp13.isFalse; 409 410 const float resErr = computeFixedPointError(prec.resultBits); 411 412 const float minBound = canBeFalse ? 0.0f : 1.0f; 413 const float maxBound = canBeTrue ? 1.0f : 0.0f; 414 415 return de::inRange(result, minBound-resErr, maxBound+resErr); 416} 417 418static bool isTrilinearPCFCompareValid (const Sampler::CompareMode compareMode, 419 const TexComparePrecision& prec, 420 const Vec4& depths0, 421 const Vec4& depths1, 422 const Vec2& xBounds0, 423 const Vec2& yBounds0, 424 const Vec2& xBounds1, 425 const Vec2& yBounds1, 426 const Vec2& fBounds, 427 const float cmpReference, 428 const float result, 429 const bool isFixedPointDepth) 430{ 431 DE_ASSERT(0.0f <= xBounds0.x() && xBounds0.x() <= xBounds0.y() && xBounds0.y() <= 1.0f); 432 DE_ASSERT(0.0f <= yBounds0.x() && yBounds0.x() <= yBounds0.y() && yBounds0.y() <= 1.0f); 433 DE_ASSERT(0.0f <= xBounds1.x() && xBounds1.x() <= xBounds1.y() && xBounds1.y() <= 1.0f); 434 DE_ASSERT(0.0f <= yBounds1.x() && yBounds1.x() <= yBounds1.y() && yBounds1.y() <= 1.0f); 435 DE_ASSERT(0.0f <= fBounds.x() && fBounds.x() <= fBounds.y() && fBounds.y() <= 1.0f); 436 DE_ASSERT(prec.pcfBits > 0); 437 438 const CmpResultSet cmp00 = execCompare(compareMode, depths0[0], cmpReference, prec.referenceBits, isFixedPointDepth); 439 const CmpResultSet cmp01 = execCompare(compareMode, depths0[1], cmpReference, prec.referenceBits, isFixedPointDepth); 440 const CmpResultSet cmp02 = execCompare(compareMode, depths0[2], cmpReference, prec.referenceBits, isFixedPointDepth); 441 const CmpResultSet cmp03 = execCompare(compareMode, depths0[3], cmpReference, prec.referenceBits, isFixedPointDepth); 442 443 const CmpResultSet cmp10 = execCompare(compareMode, depths1[0], cmpReference, prec.referenceBits, isFixedPointDepth); 444 const CmpResultSet cmp11 = execCompare(compareMode, depths1[1], cmpReference, prec.referenceBits, isFixedPointDepth); 445 const CmpResultSet cmp12 = execCompare(compareMode, depths1[2], cmpReference, prec.referenceBits, isFixedPointDepth); 446 const CmpResultSet cmp13 = execCompare(compareMode, depths1[3], cmpReference, prec.referenceBits, isFixedPointDepth); 447 448 const deUint32 isTrue = (deUint32(cmp00.isTrue)<<0) 449 | (deUint32(cmp01.isTrue)<<1) 450 | (deUint32(cmp02.isTrue)<<2) 451 | (deUint32(cmp03.isTrue)<<3) 452 | (deUint32(cmp10.isTrue)<<4) 453 | (deUint32(cmp11.isTrue)<<5) 454 | (deUint32(cmp12.isTrue)<<6) 455 | (deUint32(cmp13.isTrue)<<7); 456 const deUint32 isFalse = (deUint32(cmp00.isFalse)<<0) 457 | (deUint32(cmp01.isFalse)<<1) 458 | (deUint32(cmp02.isFalse)<<2) 459 | (deUint32(cmp03.isFalse)<<3) 460 | (deUint32(cmp10.isFalse)<<4) 461 | (deUint32(cmp11.isFalse)<<5) 462 | (deUint32(cmp12.isFalse)<<6) 463 | (deUint32(cmp13.isFalse)<<7); 464 465 // Error parameters 466 const float pcfErr = computeFixedPointError(prec.pcfBits); 467 const float resErr = computeFixedPointError(prec.resultBits); 468 const float totalErr = pcfErr+resErr; 469 470 // Iterate over all valid combinations. 471 for (deUint32 comb = 0; comb < (1<<8); comb++) 472 { 473 // Filter out invalid combinations. 474 if (((comb & isTrue) | (~comb & isFalse)) != (1<<8)-1) 475 continue; 476 477 const BVec4 cmpTrue0 = extractBVec4(comb, 0); 478 const BVec4 cmpTrue1 = extractBVec4(comb, 4); 479 const Vec4 refVal0 = select(Vec4(1.0f), Vec4(0.0f), cmpTrue0); 480 const Vec4 refVal1 = select(Vec4(1.0f), Vec4(0.0f), cmpTrue1); 481 482 // Bilinear interpolation within levels. 483 const float v00 = bilinearInterpolate(refVal0, xBounds0.x(), yBounds0.x()); 484 const float v01 = bilinearInterpolate(refVal0, xBounds0.y(), yBounds0.x()); 485 const float v02 = bilinearInterpolate(refVal0, xBounds0.x(), yBounds0.y()); 486 const float v03 = bilinearInterpolate(refVal0, xBounds0.y(), yBounds0.y()); 487 const float minV0 = de::min(v00, de::min(v01, de::min(v02, v03))); 488 const float maxV0 = de::max(v00, de::max(v01, de::max(v02, v03))); 489 490 const float v10 = bilinearInterpolate(refVal1, xBounds1.x(), yBounds1.x()); 491 const float v11 = bilinearInterpolate(refVal1, xBounds1.y(), yBounds1.x()); 492 const float v12 = bilinearInterpolate(refVal1, xBounds1.x(), yBounds1.y()); 493 const float v13 = bilinearInterpolate(refVal1, xBounds1.y(), yBounds1.y()); 494 const float minV1 = de::min(v10, de::min(v11, de::min(v12, v13))); 495 const float maxV1 = de::max(v10, de::max(v11, de::max(v12, v13))); 496 497 // Compute min-max bounds by filtering between minimum bounds and maximum bounds between levels. 498 // HW can end up choosing pretty much any of samples between levels, and thus interpolating 499 // between minimums should yield lower bound for range, and same for upper bound. 500 // \todo [2013-07-17 pyry] This seems separable? Can this be optimized? At least ranges could be pre-computed and later combined. 501 const float minF0 = minV0*(1.0f-fBounds.x()) + minV1*fBounds.x(); 502 const float minF1 = minV0*(1.0f-fBounds.y()) + minV1*fBounds.y(); 503 const float maxF0 = maxV0*(1.0f-fBounds.x()) + maxV1*fBounds.x(); 504 const float maxF1 = maxV0*(1.0f-fBounds.y()) + maxV1*fBounds.y(); 505 506 const float minF = de::min(minF0, minF1); 507 const float maxF = de::max(maxF0, maxF1); 508 509 const float minR = minF-totalErr; 510 const float maxR = maxF+totalErr; 511 512 if (de::inRange(result, minR, maxR)) 513 return true; 514 } 515 516 return false; 517} 518 519static bool isTrilinearCompareValid (const Sampler::CompareMode compareMode, 520 const TexComparePrecision& prec, 521 const Vec4& depths0, 522 const Vec4& depths1, 523 const Vec2& xBounds0, 524 const Vec2& yBounds0, 525 const Vec2& xBounds1, 526 const Vec2& yBounds1, 527 const Vec2& fBounds, 528 const float cmpReference, 529 const float result, 530 const bool isFixedPointDepth) 531{ 532 if (prec.pcfBits > 0) 533 return isTrilinearPCFCompareValid(compareMode, prec, depths0, depths1, xBounds0, yBounds0, xBounds1, yBounds1, fBounds, cmpReference, result, isFixedPointDepth); 534 else 535 return isTrilinearAnyCompareValid(compareMode, prec, depths0, depths1, cmpReference, result, isFixedPointDepth); 536} 537 538static bool isNearestCompareResultValid (const ConstPixelBufferAccess& level, 539 const Sampler& sampler, 540 const TexComparePrecision& prec, 541 const Vec2& coord, 542 const int coordZ, 543 const float cmpReference, 544 const float result) 545{ 546 const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level.getFormat()); 547 const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(), prec.coordBits.x(), prec.uvwBits.x()); 548 const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(), prec.coordBits.y(), prec.uvwBits.y()); 549 550 // Integer coordinates - without wrap mode 551 const int minI = deFloorFloatToInt32(uBounds.x()); 552 const int maxI = deFloorFloatToInt32(uBounds.y()); 553 const int minJ = deFloorFloatToInt32(vBounds.x()); 554 const int maxJ = deFloorFloatToInt32(vBounds.y()); 555 556 for (int j = minJ; j <= maxJ; j++) 557 { 558 for (int i = minI; i <= maxI; i++) 559 { 560 const int x = wrap(sampler.wrapS, i, level.getWidth()); 561 const int y = wrap(sampler.wrapT, j, level.getHeight()); 562 const float depth = lookupDepth(level, sampler, x, y, coordZ); 563 const CmpResultSet resSet = execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth); 564 565 if (isResultInSet(resSet, result, prec.resultBits)) 566 return true; 567 } 568 } 569 570 return false; 571} 572 573static bool isLinearCompareResultValid (const ConstPixelBufferAccess& level, 574 const Sampler& sampler, 575 const TexComparePrecision& prec, 576 const Vec2& coord, 577 const int coordZ, 578 const float cmpReference, 579 const float result) 580{ 581 const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level.getFormat()); 582 const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getWidth(), coord.x(), prec.coordBits.x(), prec.uvwBits.x()); 583 const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, level.getHeight(), coord.y(), prec.coordBits.y(), prec.uvwBits.y()); 584 585 // Integer coordinate bounds for (x0,y0) - without wrap mode 586 const int minI = deFloorFloatToInt32(uBounds.x()-0.5f); 587 const int maxI = deFloorFloatToInt32(uBounds.y()-0.5f); 588 const int minJ = deFloorFloatToInt32(vBounds.x()-0.5f); 589 const int maxJ = deFloorFloatToInt32(vBounds.y()-0.5f); 590 591 const int w = level.getWidth(); 592 const int h = level.getHeight(); 593 594 // \todo [2013-07-03 pyry] This could be optimized by first computing ranges based on wrap mode. 595 596 for (int j = minJ; j <= maxJ; j++) 597 { 598 for (int i = minI; i <= maxI; i++) 599 { 600 // Wrapped coordinates 601 const int x0 = wrap(sampler.wrapS, i , w); 602 const int x1 = wrap(sampler.wrapS, i+1, w); 603 const int y0 = wrap(sampler.wrapT, j , h); 604 const int y1 = wrap(sampler.wrapT, j+1, h); 605 606 // Bounds for filtering factors 607 const float minA = de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f); 608 const float maxA = de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f); 609 const float minB = de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f); 610 const float maxB = de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f); 611 612 const Vec4 depths (lookupDepth(level, sampler, x0, y0, coordZ), 613 lookupDepth(level, sampler, x1, y0, coordZ), 614 lookupDepth(level, sampler, x0, y1, coordZ), 615 lookupDepth(level, sampler, x1, y1, coordZ)); 616 617 if (isBilinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), Vec2(minB, maxB), cmpReference, result, isFixedPointDepth)) 618 return true; 619 } 620 } 621 622 return false; 623} 624 625static bool isLevelCompareResultValid (const ConstPixelBufferAccess& level, 626 const Sampler& sampler, 627 const Sampler::FilterMode filterMode, 628 const TexComparePrecision& prec, 629 const Vec2& coord, 630 const int coordZ, 631 const float cmpReference, 632 const float result) 633{ 634 if (filterMode == Sampler::LINEAR) 635 return isLinearCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result); 636 else 637 return isNearestCompareResultValid(level, sampler, prec, coord, coordZ, cmpReference, result); 638} 639 640static bool isNearestMipmapLinearCompareResultValid (const ConstPixelBufferAccess& level0, 641 const ConstPixelBufferAccess& level1, 642 const Sampler& sampler, 643 const TexComparePrecision& prec, 644 const Vec2& coord, 645 const int coordZ, 646 const Vec2& fBounds, 647 const float cmpReference, 648 const float result) 649{ 650 const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level0.getFormat()); 651 652 const int w0 = level0.getWidth(); 653 const int w1 = level1.getWidth(); 654 const int h0 = level0.getHeight(); 655 const int h1 = level1.getHeight(); 656 657 const Vec2 uBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x()); 658 const Vec2 uBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x()); 659 const Vec2 vBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord.y(), prec.coordBits.y(), prec.uvwBits.y()); 660 const Vec2 vBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord.y(), prec.coordBits.y(), prec.uvwBits.y()); 661 662 // Integer coordinates - without wrap mode 663 const int minI0 = deFloorFloatToInt32(uBounds0.x()); 664 const int maxI0 = deFloorFloatToInt32(uBounds0.y()); 665 const int minI1 = deFloorFloatToInt32(uBounds1.x()); 666 const int maxI1 = deFloorFloatToInt32(uBounds1.y()); 667 const int minJ0 = deFloorFloatToInt32(vBounds0.x()); 668 const int maxJ0 = deFloorFloatToInt32(vBounds0.y()); 669 const int minJ1 = deFloorFloatToInt32(vBounds1.x()); 670 const int maxJ1 = deFloorFloatToInt32(vBounds1.y()); 671 672 for (int j0 = minJ0; j0 <= maxJ0; j0++) 673 { 674 for (int i0 = minI0; i0 <= maxI0; i0++) 675 { 676 const float depth0 = lookupDepth(level0, sampler, wrap(sampler.wrapS, i0, w0), wrap(sampler.wrapT, j0, h0), coordZ); 677 678 for (int j1 = minJ1; j1 <= maxJ1; j1++) 679 { 680 for (int i1 = minI1; i1 <= maxI1; i1++) 681 { 682 const float depth1 = lookupDepth(level1, sampler, wrap(sampler.wrapS, i1, w1), wrap(sampler.wrapT, j1, h1), coordZ); 683 684 if (isLinearCompareValid(sampler.compare, prec, Vec2(depth0, depth1), fBounds, cmpReference, result, isFixedPointDepth)) 685 return true; 686 } 687 } 688 } 689 } 690 691 return false; 692} 693 694static bool isLinearMipmapLinearCompareResultValid (const ConstPixelBufferAccess& level0, 695 const ConstPixelBufferAccess& level1, 696 const Sampler& sampler, 697 const TexComparePrecision& prec, 698 const Vec2& coord, 699 const int coordZ, 700 const Vec2& fBounds, 701 const float cmpReference, 702 const float result) 703{ 704 const bool isFixedPointDepth = isFixedPointDepthTextureFormat(level0.getFormat()); 705 706 // \todo [2013-07-04 pyry] This is strictly not correct as coordinates between levels should be dependent. 707 // Right now this allows pairing any two valid bilinear quads. 708 709 const int w0 = level0.getWidth(); 710 const int w1 = level1.getWidth(); 711 const int h0 = level0.getHeight(); 712 const int h1 = level1.getHeight(); 713 714 const Vec2 uBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, w0, coord.x(), prec.coordBits.x(), prec.uvwBits.x()); 715 const Vec2 uBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, w1, coord.x(), prec.coordBits.x(), prec.uvwBits.x()); 716 const Vec2 vBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, h0, coord.y(), prec.coordBits.y(), prec.uvwBits.y()); 717 const Vec2 vBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, h1, coord.y(), prec.coordBits.y(), prec.uvwBits.y()); 718 719 // Integer coordinates - without wrap mode 720 const int minI0 = deFloorFloatToInt32(uBounds0.x()-0.5f); 721 const int maxI0 = deFloorFloatToInt32(uBounds0.y()-0.5f); 722 const int minI1 = deFloorFloatToInt32(uBounds1.x()-0.5f); 723 const int maxI1 = deFloorFloatToInt32(uBounds1.y()-0.5f); 724 const int minJ0 = deFloorFloatToInt32(vBounds0.x()-0.5f); 725 const int maxJ0 = deFloorFloatToInt32(vBounds0.y()-0.5f); 726 const int minJ1 = deFloorFloatToInt32(vBounds1.x()-0.5f); 727 const int maxJ1 = deFloorFloatToInt32(vBounds1.y()-0.5f); 728 729 for (int j0 = minJ0; j0 <= maxJ0; j0++) 730 { 731 for (int i0 = minI0; i0 <= maxI0; i0++) 732 { 733 const float minA0 = de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f); 734 const float maxA0 = de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f); 735 const float minB0 = de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f); 736 const float maxB0 = de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f); 737 Vec4 depths0; 738 739 { 740 const int x0 = wrap(sampler.wrapS, i0 , w0); 741 const int x1 = wrap(sampler.wrapS, i0+1, w0); 742 const int y0 = wrap(sampler.wrapT, j0 , h0); 743 const int y1 = wrap(sampler.wrapT, j0+1, h0); 744 745 depths0[0] = lookupDepth(level0, sampler, x0, y0, coordZ); 746 depths0[1] = lookupDepth(level0, sampler, x1, y0, coordZ); 747 depths0[2] = lookupDepth(level0, sampler, x0, y1, coordZ); 748 depths0[3] = lookupDepth(level0, sampler, x1, y1, coordZ); 749 } 750 751 for (int j1 = minJ1; j1 <= maxJ1; j1++) 752 { 753 for (int i1 = minI1; i1 <= maxI1; i1++) 754 { 755 const float minA1 = de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f); 756 const float maxA1 = de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f); 757 const float minB1 = de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f); 758 const float maxB1 = de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f); 759 Vec4 depths1; 760 761 { 762 const int x0 = wrap(sampler.wrapS, i1 , w1); 763 const int x1 = wrap(sampler.wrapS, i1+1, w1); 764 const int y0 = wrap(sampler.wrapT, j1 , h1); 765 const int y1 = wrap(sampler.wrapT, j1+1, h1); 766 767 depths1[0] = lookupDepth(level1, sampler, x0, y0, coordZ); 768 depths1[1] = lookupDepth(level1, sampler, x1, y0, coordZ); 769 depths1[2] = lookupDepth(level1, sampler, x0, y1, coordZ); 770 depths1[3] = lookupDepth(level1, sampler, x1, y1, coordZ); 771 } 772 773 if (isTrilinearCompareValid(sampler.compare, prec, depths0, depths1, 774 Vec2(minA0, maxA0), Vec2(minB0, maxB0), 775 Vec2(minA1, maxA1), Vec2(minB1, maxB1), 776 fBounds, cmpReference, result, isFixedPointDepth)) 777 return true; 778 } 779 } 780 } 781 } 782 783 return false; 784} 785 786static bool isMipmapLinearCompareResultValid (const ConstPixelBufferAccess& level0, 787 const ConstPixelBufferAccess& level1, 788 const Sampler& sampler, 789 const Sampler::FilterMode levelFilter, 790 const TexComparePrecision& prec, 791 const Vec2& coord, 792 const int coordZ, 793 const Vec2& fBounds, 794 const float cmpReference, 795 const float result) 796{ 797 if (levelFilter == Sampler::LINEAR) 798 return isLinearMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, cmpReference, result); 799 else 800 return isNearestMipmapLinearCompareResultValid(level0, level1, sampler, prec, coord, coordZ, fBounds, cmpReference, result); 801} 802 803bool isTexCompareResultValid (const Texture2DView& texture, 804 const Sampler& sampler, 805 const TexComparePrecision& prec, 806 const Vec2& coord, 807 const Vec2& lodBounds, 808 const float cmpReference, 809 const float result) 810{ 811 const float minLod = lodBounds.x(); 812 const float maxLod = lodBounds.y(); 813 const bool canBeMagnified = minLod <= sampler.lodThreshold; 814 const bool canBeMinified = maxLod > sampler.lodThreshold; 815 816 DE_ASSERT(isSamplerSupported(sampler)); 817 818 if (canBeMagnified) 819 { 820 if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord, 0, cmpReference, result)) 821 return true; 822 } 823 824 if (canBeMinified) 825 { 826 const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter); 827 const bool isLinearMipmap = isLinearMipmapFilter(sampler.minFilter); 828 const int minTexLevel = 0; 829 const int maxTexLevel = texture.getNumLevels()-1; 830 831 DE_ASSERT(minTexLevel < maxTexLevel); 832 833 if (isLinearMipmap) 834 { 835 const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1); 836 const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1); 837 838 DE_ASSERT(minLevel <= maxLevel); 839 840 for (int level = minLevel; level <= maxLevel; level++) 841 { 842 const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f); 843 const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f); 844 845 if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, Vec2(minF, maxF), cmpReference, result)) 846 return true; 847 } 848 } 849 else if (isNearestMipmap) 850 { 851 // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made 852 // decision to allow floor(lod + 0.5) as well. 853 const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel); 854 const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel); 855 856 DE_ASSERT(minLevel <= maxLevel); 857 858 for (int level = minLevel; level <= maxLevel; level++) 859 { 860 if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord, 0, cmpReference, result)) 861 return true; 862 } 863 } 864 else 865 { 866 if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord, 0, cmpReference, result)) 867 return true; 868 } 869 } 870 871 return false; 872} 873 874static bool isSeamplessLinearMipmapLinearCompareResultValid (const TextureCubeView& texture, 875 const int baseLevelNdx, 876 const Sampler& sampler, 877 const TexComparePrecision& prec, 878 const CubeFaceFloatCoords& coords, 879 const Vec2& fBounds, 880 const float cmpReference, 881 const float result) 882{ 883 const bool isFixedPointDepth = isFixedPointDepthTextureFormat(texture.getLevelFace(baseLevelNdx, CUBEFACE_NEGATIVE_X).getFormat()); 884 const int size0 = texture.getLevelFace(baseLevelNdx, coords.face).getWidth(); 885 const int size1 = texture.getLevelFace(baseLevelNdx+1, coords.face).getWidth(); 886 887 const Vec2 uBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.s, prec.coordBits.x(), prec.uvwBits.x()); 888 const Vec2 uBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.s, prec.coordBits.x(), prec.uvwBits.x()); 889 const Vec2 vBounds0 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size0, coords.t, prec.coordBits.y(), prec.uvwBits.y()); 890 const Vec2 vBounds1 = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size1, coords.t, prec.coordBits.y(), prec.uvwBits.y()); 891 892 // Integer coordinates - without wrap mode 893 const int minI0 = deFloorFloatToInt32(uBounds0.x()-0.5f); 894 const int maxI0 = deFloorFloatToInt32(uBounds0.y()-0.5f); 895 const int minI1 = deFloorFloatToInt32(uBounds1.x()-0.5f); 896 const int maxI1 = deFloorFloatToInt32(uBounds1.y()-0.5f); 897 const int minJ0 = deFloorFloatToInt32(vBounds0.x()-0.5f); 898 const int maxJ0 = deFloorFloatToInt32(vBounds0.y()-0.5f); 899 const int minJ1 = deFloorFloatToInt32(vBounds1.x()-0.5f); 900 const int maxJ1 = deFloorFloatToInt32(vBounds1.y()-0.5f); 901 902 tcu::ConstPixelBufferAccess faces0[CUBEFACE_LAST]; 903 tcu::ConstPixelBufferAccess faces1[CUBEFACE_LAST]; 904 905 for (int face = 0; face < CUBEFACE_LAST; face++) 906 { 907 faces0[face] = texture.getLevelFace(baseLevelNdx, CubeFace(face)); 908 faces1[face] = texture.getLevelFace(baseLevelNdx+1, CubeFace(face)); 909 } 910 911 for (int j0 = minJ0; j0 <= maxJ0; j0++) 912 { 913 for (int i0 = minI0; i0 <= maxI0; i0++) 914 { 915 const float minA0 = de::clamp((uBounds0.x()-0.5f)-float(i0), 0.0f, 1.0f); 916 const float maxA0 = de::clamp((uBounds0.y()-0.5f)-float(i0), 0.0f, 1.0f); 917 const float minB0 = de::clamp((vBounds0.x()-0.5f)-float(j0), 0.0f, 1.0f); 918 const float maxB0 = de::clamp((vBounds0.y()-0.5f)-float(j0), 0.0f, 1.0f); 919 Vec4 depths0; 920 921 { 922 const CubeFaceIntCoords c00 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+0)), size0); 923 const CubeFaceIntCoords c10 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+0)), size0); 924 const CubeFaceIntCoords c01 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+0, j0+1)), size0); 925 const CubeFaceIntCoords c11 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i0+1, j0+1)), size0); 926 927 // If any of samples is out of both edges, implementations can do pretty much anything according to spec. 928 // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color. 929 if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST) 930 return true; 931 932 depths0[0] = lookupDepthNoBorder(faces0[c00.face], sampler, c00.s, c00.t); 933 depths0[1] = lookupDepthNoBorder(faces0[c10.face], sampler, c10.s, c10.t); 934 depths0[2] = lookupDepthNoBorder(faces0[c01.face], sampler, c01.s, c01.t); 935 depths0[3] = lookupDepthNoBorder(faces0[c11.face], sampler, c11.s, c11.t); 936 } 937 938 for (int j1 = minJ1; j1 <= maxJ1; j1++) 939 { 940 for (int i1 = minI1; i1 <= maxI1; i1++) 941 { 942 const float minA1 = de::clamp((uBounds1.x()-0.5f)-float(i1), 0.0f, 1.0f); 943 const float maxA1 = de::clamp((uBounds1.y()-0.5f)-float(i1), 0.0f, 1.0f); 944 const float minB1 = de::clamp((vBounds1.x()-0.5f)-float(j1), 0.0f, 1.0f); 945 const float maxB1 = de::clamp((vBounds1.y()-0.5f)-float(j1), 0.0f, 1.0f); 946 Vec4 depths1; 947 948 { 949 const CubeFaceIntCoords c00 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+0)), size1); 950 const CubeFaceIntCoords c10 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+0)), size1); 951 const CubeFaceIntCoords c01 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+0, j1+1)), size1); 952 const CubeFaceIntCoords c11 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i1+1, j1+1)), size1); 953 954 if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST) 955 return true; 956 957 depths1[0] = lookupDepthNoBorder(faces1[c00.face], sampler, c00.s, c00.t); 958 depths1[1] = lookupDepthNoBorder(faces1[c10.face], sampler, c10.s, c10.t); 959 depths1[2] = lookupDepthNoBorder(faces1[c01.face], sampler, c01.s, c01.t); 960 depths1[3] = lookupDepthNoBorder(faces1[c11.face], sampler, c11.s, c11.t); 961 } 962 963 964 if (isTrilinearCompareValid(sampler.compare, prec, depths0, depths1, 965 Vec2(minA0, maxA0), Vec2(minB0, maxB0), 966 Vec2(minA1, maxA1), Vec2(minB1, maxB1), 967 fBounds, cmpReference, result, isFixedPointDepth)) 968 return true; 969 } 970 } 971 } 972 } 973 974 return false; 975} 976 977static bool isCubeMipmapLinearCompareResultValid (const TextureCubeView& texture, 978 const int baseLevelNdx, 979 const Sampler& sampler, 980 const Sampler::FilterMode levelFilter, 981 const TexComparePrecision& prec, 982 const CubeFaceFloatCoords& coords, 983 const Vec2& fBounds, 984 const float cmpReference, 985 const float result) 986{ 987 if (levelFilter == Sampler::LINEAR) 988 { 989 if (sampler.seamlessCubeMap) 990 return isSeamplessLinearMipmapLinearCompareResultValid(texture, baseLevelNdx, sampler, prec, coords, fBounds, cmpReference, result); 991 else 992 return isLinearMipmapLinearCompareResultValid(texture.getLevelFace(baseLevelNdx, coords.face), 993 texture.getLevelFace(baseLevelNdx+1, coords.face), 994 sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, cmpReference, result); 995 } 996 else 997 return isNearestMipmapLinearCompareResultValid(texture.getLevelFace(baseLevelNdx, coords.face), 998 texture.getLevelFace(baseLevelNdx+1, coords.face), 999 sampler, prec, Vec2(coords.s, coords.t), 0, fBounds, cmpReference, result); 1000} 1001 1002static bool isSeamlessLinearCompareResultValid (const TextureCubeView& texture, 1003 const int levelNdx, 1004 const Sampler& sampler, 1005 const TexComparePrecision& prec, 1006 const CubeFaceFloatCoords& coords, 1007 const float cmpReference, 1008 const float result) 1009{ 1010 const bool isFixedPointDepth = isFixedPointDepthTextureFormat(texture.getLevelFace(levelNdx, CUBEFACE_NEGATIVE_X).getFormat()); 1011 const int size = texture.getLevelFace(levelNdx, coords.face).getWidth(); 1012 1013 const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x()); 1014 const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y()); 1015 1016 // Integer coordinate bounds for (x0,y0) - without wrap mode 1017 const int minI = deFloorFloatToInt32(uBounds.x()-0.5f); 1018 const int maxI = deFloorFloatToInt32(uBounds.y()-0.5f); 1019 const int minJ = deFloorFloatToInt32(vBounds.x()-0.5f); 1020 const int maxJ = deFloorFloatToInt32(vBounds.y()-0.5f); 1021 1022 // Face accesses 1023 ConstPixelBufferAccess faces[CUBEFACE_LAST]; 1024 for (int face = 0; face < CUBEFACE_LAST; face++) 1025 faces[face] = texture.getLevelFace(levelNdx, CubeFace(face)); 1026 1027 for (int j = minJ; j <= maxJ; j++) 1028 { 1029 for (int i = minI; i <= maxI; i++) 1030 { 1031 const CubeFaceIntCoords c00 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+0)), size); 1032 const CubeFaceIntCoords c10 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+0)), size); 1033 const CubeFaceIntCoords c01 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+0, j+1)), size); 1034 const CubeFaceIntCoords c11 = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, IVec2(i+1, j+1)), size); 1035 1036 // If any of samples is out of both edges, implementations can do pretty much anything according to spec. 1037 // \todo [2013-07-08 pyry] Test the special case where all corner pixels have exactly the same color. 1038 if (c00.face == CUBEFACE_LAST || c01.face == CUBEFACE_LAST || c10.face == CUBEFACE_LAST || c11.face == CUBEFACE_LAST) 1039 return true; 1040 1041 // Bounds for filtering factors 1042 const float minA = de::clamp((uBounds.x()-0.5f)-float(i), 0.0f, 1.0f); 1043 const float maxA = de::clamp((uBounds.y()-0.5f)-float(i), 0.0f, 1.0f); 1044 const float minB = de::clamp((vBounds.x()-0.5f)-float(j), 0.0f, 1.0f); 1045 const float maxB = de::clamp((vBounds.y()-0.5f)-float(j), 0.0f, 1.0f); 1046 1047 Vec4 depths; 1048 depths[0] = lookupDepthNoBorder(faces[c00.face], sampler, c00.s, c00.t); 1049 depths[1] = lookupDepthNoBorder(faces[c10.face], sampler, c10.s, c10.t); 1050 depths[2] = lookupDepthNoBorder(faces[c01.face], sampler, c01.s, c01.t); 1051 depths[3] = lookupDepthNoBorder(faces[c11.face], sampler, c11.s, c11.t); 1052 1053 if (isBilinearCompareValid(sampler.compare, prec, depths, Vec2(minA, maxA), Vec2(minB, maxB), cmpReference, result, isFixedPointDepth)) 1054 return true; 1055 } 1056 } 1057 1058 return false; 1059} 1060 1061static bool isCubeLevelCompareResultValid (const TextureCubeView& texture, 1062 const int levelNdx, 1063 const Sampler& sampler, 1064 const Sampler::FilterMode filterMode, 1065 const TexComparePrecision& prec, 1066 const CubeFaceFloatCoords& coords, 1067 const float cmpReference, 1068 const float result) 1069{ 1070 if (filterMode == Sampler::LINEAR) 1071 { 1072 if (sampler.seamlessCubeMap) 1073 return isSeamlessLinearCompareResultValid(texture, levelNdx, sampler, prec, coords, cmpReference, result); 1074 else 1075 return isLinearCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec, Vec2(coords.s, coords.t), 0, cmpReference, result); 1076 } 1077 else 1078 return isNearestCompareResultValid(texture.getLevelFace(levelNdx, coords.face), sampler, prec, Vec2(coords.s, coords.t), 0, cmpReference, result); 1079} 1080 1081bool isTexCompareResultValid (const TextureCubeView& texture, const Sampler& sampler, const TexComparePrecision& prec, const Vec3& coord, const Vec2& lodBounds, const float cmpReference, const float result) 1082{ 1083 int numPossibleFaces = 0; 1084 CubeFace possibleFaces[CUBEFACE_LAST]; 1085 1086 DE_ASSERT(isSamplerSupported(sampler)); 1087 1088 getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces); 1089 1090 if (numPossibleFaces == 0) 1091 return true; // Result is undefined. 1092 1093 for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++) 1094 { 1095 const CubeFaceFloatCoords faceCoords (possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord)); 1096 const float minLod = lodBounds.x(); 1097 const float maxLod = lodBounds.y(); 1098 const bool canBeMagnified = minLod <= sampler.lodThreshold; 1099 const bool canBeMinified = maxLod > sampler.lodThreshold; 1100 1101 if (canBeMagnified) 1102 { 1103 if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.magFilter, prec, faceCoords, cmpReference, result)) 1104 return true; 1105 } 1106 1107 if (canBeMinified) 1108 { 1109 const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter); 1110 const bool isLinearMipmap = isLinearMipmapFilter(sampler.minFilter); 1111 const int minTexLevel = 0; 1112 const int maxTexLevel = texture.getNumLevels()-1; 1113 1114 DE_ASSERT(minTexLevel < maxTexLevel); 1115 1116 if (isLinearMipmap) 1117 { 1118 const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1); 1119 const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1); 1120 1121 DE_ASSERT(minLevel <= maxLevel); 1122 1123 for (int level = minLevel; level <= maxLevel; level++) 1124 { 1125 const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f); 1126 const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f); 1127 1128 if (isCubeMipmapLinearCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, Vec2(minF, maxF), cmpReference, result)) 1129 return true; 1130 } 1131 } 1132 else if (isNearestMipmap) 1133 { 1134 // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made 1135 // decision to allow floor(lod + 0.5) as well. 1136 const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel); 1137 const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel); 1138 1139 DE_ASSERT(minLevel <= maxLevel); 1140 1141 for (int level = minLevel; level <= maxLevel; level++) 1142 { 1143 if (isCubeLevelCompareResultValid(texture, level, sampler, getLevelFilter(sampler.minFilter), prec, faceCoords, cmpReference, result)) 1144 return true; 1145 } 1146 } 1147 else 1148 { 1149 if (isCubeLevelCompareResultValid(texture, 0, sampler, sampler.minFilter, prec, faceCoords, cmpReference, result)) 1150 return true; 1151 } 1152 } 1153 } 1154 1155 return false; 1156} 1157 1158bool isTexCompareResultValid (const Texture2DArrayView& texture, const Sampler& sampler, const TexComparePrecision& prec, const Vec3& coord, const Vec2& lodBounds, const float cmpReference, const float result) 1159{ 1160 const float depthErr = computeFloatingPointError(coord.z(), prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z()); 1161 const float minZ = coord.z()-depthErr; 1162 const float maxZ = coord.z()+depthErr; 1163 const int minLayer = de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers()-1); 1164 const int maxLayer = de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers()-1); 1165 1166 DE_ASSERT(isSamplerSupported(sampler)); 1167 1168 for (int layer = minLayer; layer <= maxLayer; layer++) 1169 { 1170 const float minLod = lodBounds.x(); 1171 const float maxLod = lodBounds.y(); 1172 const bool canBeMagnified = minLod <= sampler.lodThreshold; 1173 const bool canBeMinified = maxLod > sampler.lodThreshold; 1174 1175 if (canBeMagnified) 1176 { 1177 if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.magFilter, prec, coord.swizzle(0,1), layer, cmpReference, result)) 1178 return true; 1179 } 1180 1181 if (canBeMinified) 1182 { 1183 const bool isNearestMipmap = isNearestMipmapFilter(sampler.minFilter); 1184 const bool isLinearMipmap = isLinearMipmapFilter(sampler.minFilter); 1185 const int minTexLevel = 0; 1186 const int maxTexLevel = texture.getNumLevels()-1; 1187 1188 DE_ASSERT(minTexLevel < maxTexLevel); 1189 1190 if (isLinearMipmap) 1191 { 1192 const int minLevel = de::clamp((int)deFloatFloor(minLod), minTexLevel, maxTexLevel-1); 1193 const int maxLevel = de::clamp((int)deFloatFloor(maxLod), minTexLevel, maxTexLevel-1); 1194 1195 DE_ASSERT(minLevel <= maxLevel); 1196 1197 for (int level = minLevel; level <= maxLevel; level++) 1198 { 1199 const float minF = de::clamp(minLod - float(level), 0.0f, 1.0f); 1200 const float maxF = de::clamp(maxLod - float(level), 0.0f, 1.0f); 1201 1202 if (isMipmapLinearCompareResultValid(texture.getLevel(level), texture.getLevel(level+1), sampler, getLevelFilter(sampler.minFilter), prec, coord.swizzle(0,1), layer, Vec2(minF, maxF), cmpReference, result)) 1203 return true; 1204 } 1205 } 1206 else if (isNearestMipmap) 1207 { 1208 // \note The accurate formula for nearest mipmapping is level = ceil(lod + 0.5) - 1 but Khronos has made 1209 // decision to allow floor(lod + 0.5) as well. 1210 const int minLevel = de::clamp((int)deFloatCeil(minLod + 0.5f) - 1, minTexLevel, maxTexLevel); 1211 const int maxLevel = de::clamp((int)deFloatFloor(maxLod + 0.5f), minTexLevel, maxTexLevel); 1212 1213 DE_ASSERT(minLevel <= maxLevel); 1214 1215 for (int level = minLevel; level <= maxLevel; level++) 1216 { 1217 if (isLevelCompareResultValid(texture.getLevel(level), sampler, getLevelFilter(sampler.minFilter), prec, coord.swizzle(0,1), layer, cmpReference, result)) 1218 return true; 1219 } 1220 } 1221 else 1222 { 1223 if (isLevelCompareResultValid(texture.getLevel(0), sampler, sampler.minFilter, prec, coord.swizzle(0,1), layer, cmpReference, result)) 1224 return true; 1225 } 1226 } 1227 } 1228 1229 return false; 1230} 1231 1232static bool isGatherOffsetsCompareResultValid (const ConstPixelBufferAccess& texture, 1233 const Sampler& sampler, 1234 const TexComparePrecision& prec, 1235 const Vec2& coord, 1236 int coordZ, 1237 const IVec2 (&offsets)[4], 1238 float cmpReference, 1239 const Vec4& result) 1240{ 1241 const bool isFixedPointDepth = isFixedPointDepthTextureFormat(texture.getFormat()); 1242 const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, texture.getWidth(), coord.x(), prec.coordBits.x(), prec.uvwBits.x()); 1243 const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, texture.getHeight(), coord.y(), prec.coordBits.y(), prec.uvwBits.y()); 1244 1245 // Integer coordinate bounds for (x0, y0) - without wrap mode 1246 const int minI = deFloorFloatToInt32(uBounds.x()-0.5f); 1247 const int maxI = deFloorFloatToInt32(uBounds.y()-0.5f); 1248 const int minJ = deFloorFloatToInt32(vBounds.x()-0.5f); 1249 const int maxJ = deFloorFloatToInt32(vBounds.y()-0.5f); 1250 1251 const int w = texture.getWidth(); 1252 const int h = texture.getHeight(); 1253 1254 for (int j = minJ; j <= maxJ; j++) 1255 { 1256 for (int i = minI; i <= maxI; i++) 1257 { 1258 bool isCurrentPixelValid = true; 1259 1260 for (int offNdx = 0; offNdx < 4 && isCurrentPixelValid; offNdx++) 1261 { 1262 // offNdx-th coordinate offset and then wrapped. 1263 const int x = wrap(sampler.wrapS, i+offsets[offNdx].x(), w); 1264 const int y = wrap(sampler.wrapT, j+offsets[offNdx].y(), h); 1265 const float depth = lookupDepth(texture, sampler, x, y, coordZ); 1266 const CmpResultSet resSet = execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth); 1267 1268 if (!isResultInSet(resSet, result[offNdx], prec.resultBits)) 1269 isCurrentPixelValid = false; 1270 } 1271 1272 if (isCurrentPixelValid) 1273 return true; 1274 } 1275 } 1276 1277 return false; 1278} 1279 1280bool isGatherOffsetsCompareResultValid (const Texture2DView& texture, 1281 const Sampler& sampler, 1282 const TexComparePrecision& prec, 1283 const Vec2& coord, 1284 const IVec2 (&offsets)[4], 1285 float cmpReference, 1286 const Vec4& result) 1287{ 1288 DE_ASSERT(isSamplerSupported(sampler)); 1289 1290 return isGatherOffsetsCompareResultValid(texture.getLevel(0), sampler, prec, coord, 0, offsets, cmpReference, result); 1291} 1292 1293bool isGatherOffsetsCompareResultValid (const Texture2DArrayView& texture, 1294 const Sampler& sampler, 1295 const TexComparePrecision& prec, 1296 const Vec3& coord, 1297 const IVec2 (&offsets)[4], 1298 float cmpReference, 1299 const Vec4& result) 1300{ 1301 const float depthErr = computeFloatingPointError(coord.z(), prec.coordBits.z()) + computeFixedPointError(prec.uvwBits.z()); 1302 const float minZ = coord.z()-depthErr; 1303 const float maxZ = coord.z()+depthErr; 1304 const int minLayer = de::clamp(deFloorFloatToInt32(minZ + 0.5f), 0, texture.getNumLayers()-1); 1305 const int maxLayer = de::clamp(deFloorFloatToInt32(maxZ + 0.5f), 0, texture.getNumLayers()-1); 1306 1307 DE_ASSERT(isSamplerSupported(sampler)); 1308 1309 for (int layer = minLayer; layer <= maxLayer; layer++) 1310 { 1311 if (isGatherOffsetsCompareResultValid(texture.getLevel(0), sampler, prec, coord.swizzle(0,1), layer, offsets, cmpReference, result)) 1312 return true; 1313 } 1314 return false; 1315} 1316 1317static bool isGatherCompareResultValid (const TextureCubeView& texture, 1318 const Sampler& sampler, 1319 const TexComparePrecision& prec, 1320 const CubeFaceFloatCoords& coords, 1321 float cmpReference, 1322 const Vec4& result) 1323{ 1324 const bool isFixedPointDepth = isFixedPointDepthTextureFormat(texture.getLevelFace(0, coords.face).getFormat()); 1325 const int size = texture.getLevelFace(0, coords.face).getWidth(); 1326 const Vec2 uBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.s, prec.coordBits.x(), prec.uvwBits.x()); 1327 const Vec2 vBounds = computeNonNormalizedCoordBounds(sampler.normalizedCoords, size, coords.t, prec.coordBits.y(), prec.uvwBits.y()); 1328 1329 // Integer coordinate bounds for (x0,y0) - without wrap mode 1330 const int minI = deFloorFloatToInt32(uBounds.x()-0.5f); 1331 const int maxI = deFloorFloatToInt32(uBounds.y()-0.5f); 1332 const int minJ = deFloorFloatToInt32(vBounds.x()-0.5f); 1333 const int maxJ = deFloorFloatToInt32(vBounds.y()-0.5f); 1334 1335 // Face accesses 1336 ConstPixelBufferAccess faces[CUBEFACE_LAST]; 1337 for (int face = 0; face < CUBEFACE_LAST; face++) 1338 faces[face] = texture.getLevelFace(0, CubeFace(face)); 1339 1340 for (int j = minJ; j <= maxJ; j++) 1341 { 1342 for (int i = minI; i <= maxI; i++) 1343 { 1344 static const IVec2 offsets[4] = 1345 { 1346 IVec2(0, 1), 1347 IVec2(1, 1), 1348 IVec2(1, 0), 1349 IVec2(0, 0) 1350 }; 1351 1352 bool isCurrentPixelValid = true; 1353 1354 for (int offNdx = 0; offNdx < 4 && isCurrentPixelValid; offNdx++) 1355 { 1356 const CubeFaceIntCoords c = remapCubeEdgeCoords(CubeFaceIntCoords(coords.face, i+offsets[offNdx].x(), j+offsets[offNdx].y()), size); 1357 // If any of samples is out of both edges, implementations can do pretty much anything according to spec. 1358 // \todo [2014-06-05 nuutti] Test the special case where all corner pixels have exactly the same color. 1359 // See also isSeamlessLinearCompareResultValid and similar. 1360 if (c.face == CUBEFACE_LAST) 1361 return true; 1362 1363 const float depth = lookupDepthNoBorder(faces[c.face], sampler, c.s, c.t); 1364 const CmpResultSet resSet = execCompare(sampler.compare, depth, cmpReference, prec.referenceBits, isFixedPointDepth); 1365 1366 if (!isResultInSet(resSet, result[offNdx], prec.resultBits)) 1367 isCurrentPixelValid = false; 1368 } 1369 1370 if (isCurrentPixelValid) 1371 return true; 1372 } 1373 } 1374 1375 return false; 1376} 1377 1378bool isGatherCompareResultValid (const TextureCubeView& texture, 1379 const Sampler& sampler, 1380 const TexComparePrecision& prec, 1381 const Vec3& coord, 1382 float cmpReference, 1383 const Vec4& result) 1384{ 1385 int numPossibleFaces = 0; 1386 CubeFace possibleFaces[CUBEFACE_LAST]; 1387 1388 DE_ASSERT(isSamplerSupported(sampler)); 1389 1390 getPossibleCubeFaces(coord, prec.coordBits, &possibleFaces[0], numPossibleFaces); 1391 1392 if (numPossibleFaces == 0) 1393 return true; // Result is undefined. 1394 1395 for (int tryFaceNdx = 0; tryFaceNdx < numPossibleFaces; tryFaceNdx++) 1396 { 1397 const CubeFaceFloatCoords faceCoords(possibleFaces[tryFaceNdx], projectToFace(possibleFaces[tryFaceNdx], coord)); 1398 1399 if (isGatherCompareResultValid(texture, sampler, prec, faceCoords, cmpReference, result)) 1400 return true; 1401 } 1402 1403 return false; 1404} 1405 1406} // tcu 1407