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