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